diff --git a/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs b/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs index 99b5f598..81737ddd 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs @@ -23,6 +23,9 @@ */ using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; +using VRC.SDK3.Avatars.Components; namespace nadena.dev.modular_avatar.core.editor { @@ -45,10 +48,10 @@ namespace nadena.dev.modular_avatar.core.editor { EditorApplication.playModeStateChanged += OnPlayModeStateChanged; RuntimeUtil.OnDemandProcessAvatar = MaybeProcessAvatar; - Menu.SetChecked(MENU_NAME, ModularAvatarSettings.applyOnPlay); + EditorApplication.delayCall += () => Menu.SetChecked(MENU_NAME, ModularAvatarSettings.applyOnPlay); } - private static void MaybeProcessAvatar(RuntimeUtil.OnDemandSource source, AvatarTagComponent component) + private static void MaybeProcessAvatar(RuntimeUtil.OnDemandSource source, MonoBehaviour component) { if (ModularAvatarSettings.applyOnPlay && source == armedSource && component != null) { diff --git a/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs b/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs index 3ed54d4e..78622461 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs @@ -172,6 +172,12 @@ namespace nadena.dev.modular_avatar.core.editor { UnityEngine.Object.DestroyImmediate(component); } + + var activator = avatarGameObject.GetComponent(); + if (activator != null) + { + UnityEngine.Object.DestroyImmediate(activator); + } } } diff --git a/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs b/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs new file mode 100644 index 00000000..2894555f --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs @@ -0,0 +1,119 @@ +#if UNITY_EDITOR + +using System; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; +using VRC.SDK3.Avatars.Components; + +namespace nadena.dev.modular_avatar.core +{ + /// + /// This component is used to trigger MA processing upon entering play mode (prior to Av3Emu running). + /// We create it on a hidden object via AvatarTagObject's OnValidate, and it will proceed to add MAAvatarActivator + /// components to all avatar roots which contain MA components. This MAAvatarActivator component then performs MA + /// processing on Awake. + /// + /// Note that we do not directly process the avatars from MAActivator. This is to avoid processing avatars that are + /// initially inactive in the scene (which can have high overhead if the user has a lot of inactive avatars in the + /// scene). + /// + [AddComponentMenu("")] + [ExecuteInEditMode] + [DefaultExecutionOrder(-9998)] + public class Activator : MonoBehaviour + { + private const string TAG_OBJECT_NAME = "ModularAvatarInternal_Activator"; + + private void Awake() + { + if (!RuntimeUtil.isPlaying || this == null) return; + + var scene = gameObject.scene; + foreach (var root in scene.GetRootGameObjects()) + { + foreach (var avatar in root.GetComponentsInChildren()) + { + if (avatar.GetComponentInChildren(true) != null) + { + avatar.gameObject.GetOrAddComponent().hideFlags = HideFlags.HideInInspector; + } + } + } + } + + private bool HasMAComponentsInScene() + { + var scene = gameObject.scene; + foreach (var root in scene.GetRootGameObjects()) + { + if (root.GetComponentInChildren(true) != null) return true; + } + + return false; + } + + private void OnValidate() + { + EditorApplication.delayCall += () => + { + gameObject.hideFlags = HideFlags.HideInHierarchy; + if (!HasMAComponentsInScene()) + { + var scene = gameObject.scene; + DestroyImmediate(gameObject); + EditorSceneManager.MarkSceneDirty(scene); + } + }; + } + + internal static void CreateIfNotPresent(Scene scene) + { + if (!scene.IsValid() || EditorSceneManager.IsPreviewScene(scene)) return; + if (EditorApplication.isPlayingOrWillChangePlaymode) return; + + foreach (var root in scene.GetRootGameObjects()) + { + if (root.GetComponent() != null) return; + } + + var oldActiveScene = SceneManager.GetActiveScene(); + try + { + SceneManager.SetActiveScene(scene); + var gameObject = new GameObject(TAG_OBJECT_NAME); + gameObject.AddComponent(); + gameObject.hideFlags = HideFlags.HideInHierarchy; + } + finally + { + SceneManager.SetActiveScene(oldActiveScene); + } + } + } + + [AddComponentMenu("")] + [ExecuteInEditMode] + [DefaultExecutionOrder(-9997)] + public class AvatarActivator : MonoBehaviour + { + private void Awake() + { + if (!RuntimeUtil.isPlaying || this == null) return; + RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Awake, this); + } + + private void Start() + { + if (!RuntimeUtil.isPlaying || this == null) return; + RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Start, this); + } + + private void Update() + { + DestroyImmediate(this); + } + } +} +#endif \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs.meta b/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs.meta new file mode 100644 index 00000000..1254fa5f --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e41c5d0c41074360bc33887977722a4d +timeCreated: 1671337621 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs b/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs index 1f7cc400..a4e1141e 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs @@ -22,7 +22,6 @@ * SOFTWARE. */ -using System; using UnityEngine; namespace nadena.dev.modular_avatar.core @@ -44,5 +43,18 @@ namespace nadena.dev.modular_avatar.core if (!RuntimeUtil.isPlaying || this == null) return; RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Start, this); } + + private void OnValidate() + { + if (RuntimeUtil.isPlaying) return; + + RuntimeUtil.delayCall(() => + { + if (this == null) return; +#if UNITY_EDITOR + Activator.CreateIfNotPresent(gameObject.scene); +#endif + }); + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/RuntimeUtil.cs b/Packages/nadena.dev.modular-avatar/Runtime/RuntimeUtil.cs index b5b38f7e..529000ce 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/RuntimeUtil.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/RuntimeUtil.cs @@ -49,7 +49,7 @@ namespace nadena.dev.modular_avatar.core Start } - public delegate void OnDemandProcessAvatarDelegate(OnDemandSource source, AvatarTagComponent component); + public delegate void OnDemandProcessAvatarDelegate(OnDemandSource source, MonoBehaviour component); public static OnDemandProcessAvatarDelegate OnDemandProcessAvatar = (_m, _c) => { };