- Home /
It's impossible to async load a scene then have it wait while unloading another?
I'm updating my scene management code, and I thought it would be good to start asynchronously loading the next scene while the current one ends, but I've encountered a problem.
For context, I've got scriptable objects that hold scene info and transitions, and I'm doing all of this from a manager scene that stays loaded.
Here's what I had in mind:
SceneManager.SetActiveScene(managerScene.scene);
AsyncOperation newSceneLoad = SceneManager.LoadSceneAsync(newScene.scene.buildIndex, LoadSceneMode.Additive);
//This keeps it from activating while the first scene finishes
newSceneLoad.allowSceneActivation = false;
//Fade out the current scene
yield return currentScene.EndScene();
AsyncOperation unload = SceneManager.UnloadSceneAsync(currentScene.scene.buildIndex);
yield return unload; //Should wait for the first scene to unload...
newSceneLoad.allowSceneActivation = true; //This line won't work here...
yield return newSceneLoad;
//Fades in
yield return newScene.StartScene();
Here I'm using UnloadSceneAsync because UnloadScene is depreciated, but it doesn't work when used together with LoadSceneAsync. On this page it says: https://docs.unity3d.com/ScriptReference/AsyncOperation-allowSceneActivation.html
if a LoadSceneAsync.allowSceneActivation is set to false, and another AsyncOperation (e.g. SceneManager.UnloadSceneAsync ) is initialized, the last operation will not be called before the first allowSceneActivation is set to true.
That means I have to move the "allowSceneActivation = true" line up above the "yield return unload" line for unload to continue. But that means the scenes will overlap-- the first one will finishing loading before the second one is removed. And indeed when I do that I get some "There are 2 audio listeners in the scene" warnings, since the cameras for both scenes are present for a few frames.
So although it makes sense that Async loading could be used like this, it doesn't seem to be possible. Can anyone confirm this or let me know a different way to do it?
Hi! Why do you yield return
the unload
AsyncOperation
? What if you waited for it to complete using while(unload.progress < 0.9 || !unload.isDone) { yield return null; }
?
To properly do fading between scenes I ended up having the camera and AudioListener as a Do not destroy that lives outside the scenes, controlled by a Game$$anonymous$$anager. (But this doesn't solve your problem now, just a tip, but there are probably many ways to do it).
Answer by Greyborn · Feb 27, 2020 at 10:12 AM
@adsilcott, I was trying to do exactly as you were:
—
Load the new scene (but don't allow it to activate on load).
Unload the old scene.
Activate new scene.
—
... and I was experiencing the same issue with the AsyncOperation being stalled due to allowSceneActivation being false.
—
My solution was to destroy all root objects on the old scene, activate the new scene, and then unload the old scene:
—
Load the new scene (but don't allow it to activate on load).
Destroy all root objects on the old scene
Activate new scene.
Unload the old scene.
—
This allows the old scene to unload, because the new scene is no longer blocking the AsyncOperation queue. And the old scene objects being destroyed prevents the duplicate objects, such as audio listeners.
—
For the record, I'm reloading a scene (Additive) layer. If I were to unload the old scene before loading the new scene, Unity would release scene resources only to immediately reload them, increasing load times. However, loading the new scene while the old scene is loaded allows Unity to skip loading certain assets that exist in the old scene, which speeds up load times.
—
I'm also using the new Addressables.LoadSceneAsync(assetReference, mode, activateOnLoad)
instead of SceneManager.LoadSceneAsync()
methods, but I expect they should work the same way. I am unloading the old scene with SceneManager.UnloadSceneAsync(scene)
.
—
Anyhow, I hope that helps anyone interested in cleanly loading or reloading a scene without overlapping an old scene that should be replaced.
It's been about two years since I wrote that question, and while I've long moved on from the approach I was working on, this seems like a good answer. I may go back and try to rewrite my previous code based on this. Thanks!
Answer by Thev2Andy · Feb 21, 2020 at 05:01 PM
The loading scene process works as following. First of all, unity has a float with the scene loading progress. That float is going as following:
From 0 to 0.9: Unity loads everything that makes the next scene.
From 0.9 to 1: Unity deletes everything that makes the old scene (from the memory, not from the game) and activates everything that makes the new scene.
Unity will automatically load the next scene when the float is 1. Please reply if I missed something and sorry for my english.
If what you say is true, the real problem of my scene is in the activation, not in the loading, how can I get the progress of the scene activation?
Instead of having things that take time in Start(), which will all run at one frame and if it takes longer delay/make the frame rate stutter, you can set Time.timeScale = 0.0f; do all your things split up in Update() for how many updates/frames you need then unpause the game. But it is still tricky to avoid stuttering.
Your answer
Follow this Question
Related Questions
How Do I Link Different Scenes? 4 Answers
Change Scene keeping Character and GUI Canvas 1 Answer
Scene Loading Issue. 0 Answers
Can't find game object / script after scene re-enter 1 Answer
Does Active/Inactive and Enabled/Disabled save memory? 1 Answer