/// <summary> /// Starts loading the given extendedScene and applies the scene unload rules for its ring. /// </summary> public void StageForLoading(ExtendedScene extendedScene) { if (loadPhase == LoadPhase.Unloading) { Util.Crash(new Exception("Can't stage " + extendedScene.metadata.path + " to load because ExtendedSceneManager is in the unload phase!")); } if (extendedScene.isLoaded) { Util.Crash(new Exception(extendedScene.metadata.path + " is already loaded!")); } if (extendedScene.metadata.buildIndex == 0) { Util.Crash(new Exception("Can't reload Universe start scene!")); } if (Debug.isDebugBuild && verbose) { Debug.Log("Staged scene for loading: " + extendedScene.metadata.path + " in phase " + loadPhase); } scenesToLoad.Enqueue(extendedScene); ApplySceneRingRules(extendedScene); if (!loading) { LoadStarted(); } }
/// <summary> /// Take a build index and return a new ExtendedScene that corresponds to it. /// The new ExtendedScene will be added to the dictionaries. /// </summary> private ExtendedScene CreateExtendedSceneForIndex(int buildIndex) { ExtendedScene extendedScene = new ExtendedScene(SceneDatatable.metadata[buildIndex]); AddExtendedSceneToArray(extendedScene); return(extendedScene); }
/// <summary> /// Fetch the appropriate ruleset for the given ExtendedScene to use when loading in. /// </summary> private Action <ExtendedScene>[] AcquireSceneRingRules(ExtendedScene loadingScene) { Action <ExtendedScene>[] ruleset = new Action <ExtendedScene> [0]; switch (loadingScene.metadata.sceneRing) { case SceneRing.None: case SceneRing.GlobalScenes: break; case SceneRing.SystemScenes: ruleset = systemScenesRules; break; case SceneRing.VenueScenes: ruleset = venueScenesRules; break; case SceneRing.WorldScenes: Util.Crash(new NotImplementedException()); break; default: Util.Crash(new Exception("Invalid ring for scene " + loadingScene.metadata.path + ": " + loadingScene.metadata.sceneRing)); break; } return(ruleset); }
/// <summary> /// Gets the currently active scene as an ExtendedScene. Syncs that with the ring-active scenes. /// </summary> private void SyncActiveScene() { ExtendedScene extendedScene = GetExtendedScene(SceneManager.GetActiveScene().buildIndex); if (GetActiveScene(extendedScene.metadata.sceneRing) != extendedScene) { lastScenesActiveInRings[extendedScene.metadata.sceneRing] = extendedScene; } }
/// <summary> /// Removes the given extendedScene from the list of loaded scenes. /// </summary> public void MarkSceneUnloaded(ExtendedScene extendedScene) { if (!loadedScenes.Contains(extendedScene)) { Util.Crash(new Exception(extendedScene.metadata.path + " isn't loaded!")); } loadedScenes.Remove(extendedScene); timing.RunCoroutineOnInstance(Util._WaitOneFrame(LoadPhaseAdvance)); }
/// <summary> /// Applies the scene unload/suspend rules to all loaded scenes based on the ring of the scene that'd being loaded in. /// This allows for automation of complex behaviors like (ex:) "unload all scenes in ring x when entering scene of ring y" /// or "load only one scene in ring z" or "when loading scenes from ring a, suspend all scenes in ring b but the last active one." /// </summary> public void ApplySceneRingRules(ExtendedScene loadingScene) { Action <ExtendedScene>[] ruleset = AcquireSceneRingRules(loadingScene); for (int r = 0; r < ruleset.Length; r++) { for (int s = 0; s < loadedScenes.Count; s++) { if (loadedScenes[s] != loadingScene) { ruleset[r](loadedScenes[s]); } } } }
/// <summary> /// Starts unloading the given extendedScene. /// </summary> public void StageForUnloading(ExtendedScene extendedScene) { if (!extendedScene.isLoaded) { Util.Crash(new Exception(extendedScene.metadata.path + " isn't loaded!")); } if (Debug.isDebugBuild && verbose) { Debug.Log("Staged scene for unloading: " + extendedScene.metadata.path + " in phase " + loadPhase); } if (extendedScene.hasRootHandle) { extendedScene.SuspendScene(); } scenesToUnload.Enqueue(extendedScene); if (!loading) { LoadStarted(); } }
/// <summary> /// MonoBehaviour.Awake() /// </summary> void Awake() { SceneRing[] rings = (SceneRing[])Enum.GetValues(typeof(SceneRing)); timing = gameObject.AddComponent <Timing>(); currentLoadingOps = new List <AsyncOperation>(32); extendedScenesArray = new ExtendedScene[SceneDatatable.metadata.Length]; loadedScenes = new List <ExtendedScene>(32); scenesToLoad = new Queue <ExtendedScene>(32); scenesToUnload = new Queue <ExtendedScene>(32); scenesBufferList = new List <ExtendedScene>(128); sceneMetadataBufferList = new List <SceneMetadata>(128); lastScenesActiveInRings = new Dictionary <SceneRing, ExtendedScene>(rings.Length); scenesBySceneRings = new Dictionary <SceneRing, ExtendedScene[]>(rings.Length); Action onceOnline = () => { for (int r = 0; r < rings.Length; r++) { SceneRing ring = rings[r]; scenesBufferList.Clear(); for (int s = 0; s < SceneDatatable.metadata.Length; s++) { ExtendedScene extendedScene = GetExtendedScene(s); if (extendedScene.metadata.sceneRing == ring) { scenesBufferList.Add(extendedScene); } } scenesBySceneRings[ring] = scenesBufferList.ToArray(); lastScenesActiveInRings[ring] = null; } LoadGlobalScenes(); SceneManager.activeSceneChanged += (sceneA, sceneB) => { SyncActiveScene(); }; }; timing.RunCoroutineOnInstance(_WaitUntilOnline(onceOnline)); }
/// <summary> /// Called once we've finished a batch of loading operations. /// Either stages the next batch or marks us done, depending. /// We don't actually care about the arguments, but the sceneLoaded /// and sceneUnloaded delegates want this signature. /// </summary> private void LoadPhaseAdvance() { Action loadDone = () => { if (GetProgressOfLoad() < 1.0f) { Util.Crash(new Exception()); } if (loadPhase != LoadPhase.NotLoading) { loadPhase = LoadPhase.NotLoading; if (Debug.isDebugBuild && verbose) { Debug.Log("Completed batch scene load/unload operations."); } } }; Action <int> load = (count) => { loadPhase = LoadPhase.Loading; for (int i = 0; i < count; i++) { ExtendedScene extendedScene = scenesToLoad.Dequeue(); if (Debug.isDebugBuild && verbose) { Debug.Log("Started loading: " + extendedScene.metadata.path); } currentLoadingOps.Add(SceneManager.LoadSceneAsync(extendedScene.metadata.buildIndex, LoadSceneMode.Additive)); } }; Action <int> unload = (count) => { loadPhase = LoadPhase.Unloading; for (int i = 0; i < count; i++) { ExtendedScene extendedScene = scenesToUnload.Dequeue(); if (Debug.isDebugBuild && verbose) { Debug.Log("Started unloading: " + extendedScene.metadata.path); } currentLoadingOps.Add(SceneManager.UnloadSceneAsync(extendedScene.metadata.buildIndex)); } }; if (Debug.isDebugBuild && verbose) { Debug.Log("Phase " + loadPhase + ", progress " + GetProgressOfLoad() + ", scenes in load queue " + scenesToLoad.Count + ", scenes in unload queue" + scenesToUnload.Count + " operations in progress" + currentLoadingOps.Count); } switch (loadPhase) { case LoadPhase.NotLoading: case LoadPhase.Loading: if (scenesToLoad.Count > 0 || Util.AverageCompletionOfOps(currentLoadingOps.ToArray()) < 1.0f) { load(scenesToLoad.Count); } else if (scenesToUnload.Count > 0) { unload(scenesToUnload.Count); } else if (GetProgressOfLoad() >= 1.0f) { loadDone(); } break; case LoadPhase.Unloading: if (scenesToUnload.Count > 0 || Util.AverageCompletionOfOps(currentLoadingOps.ToArray()) < 1.0f) { unload(scenesToUnload.Count); } else if (scenesToLoad.Count > 0) { load(scenesToLoad.Count); } else if (GetProgressOfLoad() >= 1.0f) { loadDone(); } break; } }
/// <summary> /// Adds the extended scene to the datatables. /// </summary> private void AddExtendedSceneToArray(ExtendedScene extendedScene) { extendedScenesArray[extendedScene.metadata.buildIndex] = extendedScene; }
/// <summary> /// Sets active scene based on given extended scene. /// </summary> public void SetActiveScene(ExtendedScene extendedScene) { lastScenesActiveInRings[extendedScene.metadata.sceneRing] = extendedScene; SceneManager.SetActiveScene(SceneManager.GetSceneByBuildIndex(extendedScene.metadata.buildIndex)); }