Skip to content

Commit

Permalink
Merge pull request #318 from anatawa12/apply-on-play-activator
Browse files Browse the repository at this point in the history
chore: do not add GlobalActivator if there are no avatars in the scene
  • Loading branch information
anatawa12 authored Aug 12, 2023
2 parents f3ab024 + f3d8573 commit c7e06f1
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 13 additions & 2 deletions Internal/ApplyOnPlay/Editor/ApplyOnPlayCaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
}

Expand Down
2 changes: 2 additions & 0 deletions Internal/ApplyOnPlay/Runtime/ApplyOnPlayActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Anatawa12.ApplyOnPlay
{
[DefaultExecutionOrder(-100000)]
[ExecuteAlways]
[AddComponentMenu("")]
[DisallowMultipleComponent]
internal class ApplyOnPlayActivator : MonoBehaviour
{
#if UNITY_EDITOR
Expand Down
55 changes: 51 additions & 4 deletions Internal/ApplyOnPlay/Runtime/GlobalActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<VRC_AvatarDescriptor>(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<GlobalActivator>() != null)
{
DestroyImmediate(root);
EditorSceneManager.MarkSceneDirty(scene);
}
}
}
}

private static void CreateIfNotExists(Scene scene)
{
bool rootPresent = false;
foreach (var root in scene.GetRootGameObjects())
{
if (root.GetComponent<GlobalActivator>() != null)
{
root.hideFlags = HIDE_FLAGS;
root.hideFlags = HideFlags;
root.SetActive(true);
if (rootPresent) DestroyImmediate(root);
rootPresent = true;
Expand All @@ -46,15 +75,33 @@ internal static void CreateIfNotPresent(Scene scene)
SceneManager.SetActiveScene(scene);
var gameObject = new GameObject("ApplyOnPlayGlobalActivator");
gameObject.AddComponent<GlobalActivator>();
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
}
}
73 changes: 73 additions & 0 deletions Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs
Original file line number Diff line number Diff line change
@@ -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<SceneChangeReceiver>() != 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<SceneChangeReceiver>();
component.scene = scene;
gameObject.hideFlags = HideFlags;
component.hideFlags = HideFlags;
}
finally
{
SceneManager.SetActiveScene(oldActiveScene);
}
}

private const HideFlags HideFlags = UnityEngine.HideFlags.HideAndDontSave;
#endif
}
}
3 changes: 3 additions & 0 deletions Internal/ApplyOnPlay/Runtime/SceneChangeReceiver.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [],
"precompiledReferences": [
"VRCSDKBase.dll"
],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
Expand Down

0 comments on commit c7e06f1

Please sign in to comment.