diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index b173b2840..a5e938225 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog]. ### Added ### Changed +- ApplyOnPlayGlobalActivator is no longer added for scens without avatars `#318` ### Deprecated diff --git a/Internal/ApplyOnPlay/Editor/ApplyOnPlayCaller.cs b/Internal/ApplyOnPlay/Editor/ApplyOnPlayCaller.cs index f240846d2..96d361435 100644 --- a/Internal/ApplyOnPlay/Editor/ApplyOnPlayCaller.cs +++ b/Internal/ApplyOnPlay/Editor/ApplyOnPlayCaller.cs @@ -28,12 +28,23 @@ static ApplyOnPlayCaller() ApplyOnPlayCallbackRegistry.GetCallbacks()); }; - EditorSceneManager.sceneOpened += (scene, _) => GlobalActivator.CreateIfNotPresent(scene); + EditorSceneManager.newSceneCreated += (scene, _1, _2) => + EditorApplication.delayCall += () => SceneChangeReceiver.CreateIfNotExists(scene); + EditorSceneManager.sceneOpened += (scene, _) => GlobalActivator.CreateIfNotNeeded(scene); + + EditorApplication.playModeStateChanged += state => + { + if (state != PlayModeStateChange.EnteredEditMode) return; + foreach (var scene in Enumerable.Range(0, SceneManager.sceneCount) + .Select(SceneManager.GetSceneAt)) + SceneChangeReceiver.CreateIfNotNeeded(scene); + }; + EditorApplication.delayCall += () => { foreach (var scene in Enumerable.Range(0, SceneManager.sceneCount) .Select(SceneManager.GetSceneAt)) - GlobalActivator.CreateIfNotPresent(scene); + GlobalActivator.CreateIfNotNeeded(scene); }; } diff --git a/Internal/ApplyOnPlay/Runtime/ApplyOnPlayActivator.cs b/Internal/ApplyOnPlay/Runtime/ApplyOnPlayActivator.cs index 4083d161c..f6a47aef8 100644 --- a/Internal/ApplyOnPlay/Runtime/ApplyOnPlayActivator.cs +++ b/Internal/ApplyOnPlay/Runtime/ApplyOnPlayActivator.cs @@ -8,6 +8,8 @@ namespace Anatawa12.ApplyOnPlay { [DefaultExecutionOrder(-100000)] [ExecuteAlways] + [AddComponentMenu("")] + [DisallowMultipleComponent] internal class ApplyOnPlayActivator : MonoBehaviour { #if UNITY_EDITOR diff --git a/Internal/ApplyOnPlay/Runtime/GlobalActivator.cs b/Internal/ApplyOnPlay/Runtime/GlobalActivator.cs index 62ca3480f..9e3aeb7d5 100644 --- a/Internal/ApplyOnPlay/Runtime/GlobalActivator.cs +++ b/Internal/ApplyOnPlay/Runtime/GlobalActivator.cs @@ -3,13 +3,17 @@ using UnityEditor.SceneManagement; #endif using System; +using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; +using VRC.SDKBase; namespace Anatawa12.ApplyOnPlay { [DefaultExecutionOrder(-100000)] [ExecuteAlways] + [AddComponentMenu("")] + [DisallowMultipleComponent] internal class GlobalActivator : MonoBehaviour { #if UNITY_EDITOR @@ -21,17 +25,42 @@ private void Awake() activate?.Invoke(this); } - internal static void CreateIfNotPresent(Scene scene) + internal static bool HasAvatarInScene(Scene scene) + { + return scene.GetRootGameObjects().Any(x => x.GetComponentInChildren(true)); + } + + internal static void CreateIfNotNeeded(Scene scene) { if (!scene.IsValid() || EditorSceneManager.IsPreviewScene(scene)) return; if (EditorApplication.isPlayingOrWillChangePlaymode) return; + if (HasAvatarInScene(scene)) + { + CreateIfNotExists(scene); + } + else + { + SceneChangeReceiver.CreateIfNotExists(scene); + foreach (var root in scene.GetRootGameObjects()) + { + if (root.GetComponent() != null) + { + DestroyImmediate(root); + EditorSceneManager.MarkSceneDirty(scene); + } + } + } + } + + private static void CreateIfNotExists(Scene scene) + { bool rootPresent = false; foreach (var root in scene.GetRootGameObjects()) { if (root.GetComponent() != null) { - root.hideFlags = HIDE_FLAGS; + root.hideFlags = HideFlags; root.SetActive(true); if (rootPresent) DestroyImmediate(root); rootPresent = true; @@ -46,15 +75,33 @@ internal static void CreateIfNotPresent(Scene scene) SceneManager.SetActiveScene(scene); var gameObject = new GameObject("ApplyOnPlayGlobalActivator"); gameObject.AddComponent(); - gameObject.hideFlags = HIDE_FLAGS; + gameObject.hideFlags = HideFlags; } finally { SceneManager.SetActiveScene(oldActiveScene); } } + + private void OnValidate() + { + if (EditorApplication.isPlayingOrWillChangePlaymode) return; + + EditorApplication.delayCall += () => + { + if (this == null) return; + + gameObject.hideFlags = HideFlags; + if (!HasAvatarInScene(gameObject.scene)) + { + var scene = gameObject.scene; + DestroyImmediate(gameObject); + EditorSceneManager.MarkSceneDirty(scene); + } + }; + } - private const HideFlags HIDE_FLAGS = HideFlags.HideInHierarchy; + private const HideFlags HideFlags = UnityEngine.HideFlags.HideInHierarchy; #endif } } \ No newline at end of file diff --git a/Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs b/Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs new file mode 100644 index 000000000..d3ac75681 --- /dev/null +++ b/Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs @@ -0,0 +1,73 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Anatawa12.ApplyOnPlay +{ + [ExecuteAlways] + [AddComponentMenu("")] + [DisallowMultipleComponent] + public class SceneChangeReceiver : MonoBehaviour + { +#if UNITY_EDITOR + public Scene scene; + + void Update() + { + if (scene.IsValid()) + { + if (GlobalActivator.HasAvatarInScene(scene)) + { + GlobalActivator.CreateIfNotNeeded(scene); + DestroyImmediate(this); + } + } + else + { + DestroyImmediate(this); + } + } + + internal static void CreateIfNotNeeded(Scene scene) + { + if (!scene.IsValid() || UnityEditor.SceneManagement.EditorSceneManager.IsPreviewScene(scene)) return; + if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) return; + if (GlobalActivator.HasAvatarInScene(scene)) return; + CreateIfNotExists(scene); + } + + internal static void CreateIfNotExists(Scene scene) + { + bool rootPresent = false; + foreach (var root in scene.GetRootGameObjects()) + { + if (root.GetComponent() != null) + { + root.hideFlags = HideFlags; + root.SetActive(true); + if (rootPresent) DestroyImmediate(root); + rootPresent = true; + } + } + + if (rootPresent) return; + + var oldActiveScene = SceneManager.GetActiveScene(); + try + { + SceneManager.SetActiveScene(scene); + var gameObject = new GameObject("ApplyOnPlaySceneChangeReceiver"); + var component = gameObject.AddComponent(); + component.scene = scene; + gameObject.hideFlags = HideFlags; + component.hideFlags = HideFlags; + } + finally + { + SceneManager.SetActiveScene(oldActiveScene); + } + } + + private const HideFlags HideFlags = UnityEngine.HideFlags.HideAndDontSave; +#endif + } +} \ No newline at end of file diff --git a/Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs.meta b/Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs.meta new file mode 100644 index 000000000..d940e4eb2 --- /dev/null +++ b/Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 357a344b57c4469c88ba43f2342a4cb1 +timeCreated: 1691823379 \ No newline at end of file diff --git a/Internal/ApplyOnPlay/Runtime/com.anatawa12.avatar-optimizer.internal.apply-on-play.runtime.asmdef b/Internal/ApplyOnPlay/Runtime/com.anatawa12.avatar-optimizer.internal.apply-on-play.runtime.asmdef index e69942421..fb0b714d7 100644 --- a/Internal/ApplyOnPlay/Runtime/com.anatawa12.avatar-optimizer.internal.apply-on-play.runtime.asmdef +++ b/Internal/ApplyOnPlay/Runtime/com.anatawa12.avatar-optimizer.internal.apply-on-play.runtime.asmdef @@ -5,7 +5,9 @@ "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, - "precompiledReferences": [], + "precompiledReferences": [ + "VRCSDKBase.dll" + ], "autoReferenced": false, "defineConstraints": [], "versionDefines": [],