From 36f23b7cdc8dec42dd6d3e3983eed3b8cf4b1fd5 Mon Sep 17 00:00:00 2001 From: Sinai Date: Wed, 26 May 2021 17:41:51 +1000 Subject: [PATCH] Move SceneHandler.cs --- src/UI/ObjectExplorer/SceneHandler.cs | 177 ++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 src/UI/ObjectExplorer/SceneHandler.cs diff --git a/src/UI/ObjectExplorer/SceneHandler.cs b/src/UI/ObjectExplorer/SceneHandler.cs new file mode 100644 index 0000000..c6f3538 --- /dev/null +++ b/src/UI/ObjectExplorer/SceneHandler.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace UnityExplorer.UI.ObjectExplorer +{ + public static class SceneHandler + { + /// + /// The currently inspected Scene. + /// + public static Scene? SelectedScene + { + get => m_selectedScene; + internal set + { + if (m_selectedScene != null && m_selectedScene?.handle == value?.handle) + return; + m_selectedScene = value; + OnInspectedSceneChanged?.Invoke((Scene)m_selectedScene); + } + } + private static Scene? m_selectedScene; + + /// + /// The GameObjects in the currently inspected scene. + /// + public static ReadOnlyCollection CurrentRootObjects => new ReadOnlyCollection(rootObjects); + private static GameObject[] rootObjects = new GameObject[0]; + + /// + /// All currently loaded Scenes. + /// + public static ReadOnlyCollection LoadedScenes => new ReadOnlyCollection(allLoadedScenes); + private static readonly List allLoadedScenes = new List(); + + /// + /// The names of all scenes in the build settings, if they could be retrieved. + /// + public static ReadOnlyCollection AllSceneNames => new ReadOnlyCollection(allScenesInBuild); + private static readonly List allScenesInBuild = new List(); + + /// + /// Whether or not we successfuly retrieved the names of the scenes in the build settings. + /// + public static bool WasAbleToGetScenesInBuild => gotAllScenesInBuild; + private static bool gotAllScenesInBuild = true; + + /// + /// Invoked when the currently inspected Scene changes. The argument is the new scene. + /// + public static event Action OnInspectedSceneChanged; + + /// + /// Invoked whenever the list of currently loaded Scenes changes. The argument contains all loaded scenes after the change. + /// + public static event Action> OnLoadedScenesChanged; + + /// + /// Equivalent to + 2, to include 'DontDestroyOnLoad'. + /// + public static int LoadedSceneCount => SceneManager.sceneCount + 2; + + internal static Scene DontDestroyScene => DontDestroyMe.scene; + internal static int DontDestroyHandle => DontDestroyScene.handle; + + internal static GameObject DontDestroyMe + { + get + { + if (!dontDestroyObject) + { + dontDestroyObject = new GameObject("DontDestroyMe"); + GameObject.DontDestroyOnLoad(dontDestroyObject); + } + return dontDestroyObject; + } + } + private static GameObject dontDestroyObject; + + public static bool InspectingAssetScene => !SelectedScene?.IsValid() ?? false; + + internal static void Init() + { + // Try to get all scenes in the build settings. This may not work. + try + { + Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility"); + if (sceneUtil == null) + throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped."); + + var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS); + int sceneCount = SceneManager.sceneCountInBuildSettings; + for (int i = 0; i < sceneCount; i++) + { + var scenePath = (string)method.Invoke(null, new object[] { i }); + allScenesInBuild.Add(scenePath); + } + } + catch (Exception ex) + { + gotAllScenesInBuild = false; + ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}"); + } + } + + internal static void Update() + { + int curHandle = SelectedScene?.handle ?? -1; + // DontDestroyOnLoad always exists, so default to true if our curHandle is that handle. + // otherwise we will check while iterating. + bool inspectedExists = curHandle == DontDestroyHandle || curHandle == 0; + + // Quick sanity check if the loaded scenes changed + bool anyChange = LoadedSceneCount != allLoadedScenes.Count; + // otherwise keep a lookup table of the previous handles to check if the list changed at all. + HashSet previousHandles = null; + if (!anyChange) + previousHandles = new HashSet(allLoadedScenes.Select(it => it.handle)); + + allLoadedScenes.Clear(); + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene scene = SceneManager.GetSceneAt(i); + if (scene == default || !scene.isLoaded) + continue; + + // If no changes yet, ensure the previous list contained this handle. + if (!anyChange && !previousHandles.Contains(scene.handle)) + anyChange = true; + + // If we have not yet confirmed inspectedExists, check if this scene is our currently inspected one. + if (curHandle != -1 && !inspectedExists && scene.handle == curHandle) + inspectedExists = true; + + allLoadedScenes.Add(scene); + } + + // Always add the DontDestroyOnLoad scene and the "none" scene. + allLoadedScenes.Add(DontDestroyScene); + allLoadedScenes.Add(default); + + // Default to first scene if none selected or previous selection no longer exists. + if (!inspectedExists) + { + SelectedScene = allLoadedScenes.First(); + } + + // Notify on the list changing at all + if (anyChange) + { + OnLoadedScenesChanged?.Invoke(LoadedScenes); + } + + // Finally, update the root objects list. + if (SelectedScene != null && ((Scene)SelectedScene).IsValid()) + rootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene); + else + { + var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject)); + var list = new List(); + foreach (var obj in allObjects) + { + var go = obj.TryCast(); + if (go.transform.parent == null && !go.scene.IsValid()) + list.Add(go); + } + rootObjects = list.ToArray(); + } + } + } +}