using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using UnityEngine; using UnityEngine.SceneManagement; using UniverseLib; namespace UnityExplorer.ObjectExplorer { public static class SceneHandler { /// /// The currently inspected Scene. /// public static Scene? SelectedScene { get => selectedScene; internal set { if (selectedScene != null && selectedScene == value) return; selectedScene = value; OnInspectedSceneChanged?.Invoke((Scene)selectedScene); } } private static Scene? selectedScene; /// /// The GameObjects in the currently inspected scene. /// public static GameObject[] CurrentRootObjects { get; private set; } = new GameObject[0]; /// /// All currently loaded Scenes. /// public static List LoadedScenes { get; private set; } = new List(); private static HashSet previousLoadedScenes; /// /// The names of all scenes in the build settings, if they could be retrieved. /// public static readonly List AllSceneNames = new List(); /// /// Whether or not we successfuly retrieved the names of the scenes in the build settings. /// public static bool WasAbleToGetScenesInBuild { get; private set; } /// /// 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' and the 'None' scene. /// 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.HasValue && SelectedScene.Value == default; 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 }); AllSceneNames.Add(scenePath); } WasAbleToGetScenesInBuild = true; } catch (Exception ex) { WasAbleToGetScenesInBuild = false; ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}"); } } internal static void Update() { // check if the loaded scenes changed. always confirm DontDestroy / HideAndDontSave int confirmedCount = 2; bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default); LoadedScenes.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 the scene if (previousLoadedScenes != null && previousLoadedScenes.Contains(scene)) confirmedCount++; // If we have not yet confirmed inspectedExists, check if this scene is our currently inspected one. if (!inspectedExists && scene == SelectedScene) inspectedExists = true; LoadedScenes.Add(scene); } LoadedScenes.Add(DontDestroyScene); LoadedScenes.Add(default); bool anyChange = confirmedCount != LoadedScenes.Count; previousLoadedScenes = new HashSet(LoadedScenes); // Default to first scene if none selected or previous selection no longer exists. if (!inspectedExists) SelectedScene = LoadedScenes.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()) CurrentRootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene); else { var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject)); var objects = new List(); foreach (var obj in allObjects) { var go = obj.TryCast(); if (go.transform.parent == null && !go.scene.IsValid()) objects.Add(go); } CurrentRootObjects = objects.ToArray(); } } } }