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();
+ }
+ }
+ }
+}