March 25, 2019
Advancing Tower Defense Tutorial
Moving Objects and References to New Scenes
Catlike Coding – Multiple Scenes and Loading Levels Catlike Coding – Object Management
The Catlike Coding information on Multiple Scenes and Loading levels has a lot on how scenes are loaded in Unity and how to control that process better. I also included the link to the overal Object Management tutorial that this Multiple Scene write up is a part of. That seems like another useful overall source, that could also enhance the application of this Multiple Scene tutorial.
They start by creating a new scene and moving certain objects into it. After setting this up, they correct an issue they had where recompilation messes up the pool scene. This happens because they are using a scriptable object instead of the default monobehaviour for their script. Unity serializes the private fields of Monobehaviours when compiling, but not for Scriptable Objects. This meant CreatePools would get invoked on every recompilation and you would get errors trying to create the same scene several times.
To protect against this issue, they check if the scene is loaded using the Scene Manager isLoaded method. If that is true, they just return on their created method. This requires getting the scene value first since recompilation resets the poolScene scene struct to its default values on recompilation.
Level 01 – Multi-Scene Editing
Main Scene will be their scene for containing everything to run the game, no matter what level they’re playing. It contains the Main Camera, the Event System, and some other parts. Their basic Level 1 scene just contains the Light for now.
They first explore using the Scene Manager LoadScene() method to load their additional scene. This does not work, as LoadScene first unloads all currently open scenes before loading the requested scene. To load the scene in addition to what is already there, they add the LoadSceneMode.Additive as an argument to LoadScene().
The lighting is still incorrect because they need the Level 01 scene to be the active scene. This can be done in script with the SetActiveScene() method. They try placing this in their method for loading the scene, but it does not work. SetActiveScene only works for loaded scenes, and the loaded scene isn’t technically loaded yet even though it was called previously. This is because loading a scene takes some time.
To get around this, you can wait a frame between loading the scene and setting the newly loaded scene to the active scene. This can be done by making LoadLevel into a coroutine and adding a yield return null command between loading the scene and setting it active. This will give the desired result.
If your game takes a while to load, this simple load command may cause undesired results. This is because the game would basically freeze until the loading is finished. To prevent this, scenes can be loaded asynchronously, using the SceneManager.LoadSceneAsync method. This begins the process of loading a scene and returns an AsyncOperation object reference. This can be used to check whether a scene has finished loading. This can also be used to yield in a coroutine. This is how their example looks: IEnumerator LoadLevel () { //SceneManager.LoadScene(“Level 1”, LoadSceneMode.Additive); //yield return null; yield return SceneManager.LoadSceneAsync( “Level 1”, LoadSceneMode.Additive ); SceneManager.SetActiveScene(SceneManager.GetSceneByName(“Level 1”)); }
This coroutine now waits until the scene is completely loaded to continue its processes, but also does not freeze the game while loading. This however creates another issue where Update be called during the loading of the level. This would be bad for most games as you don’t want the player issuing commands before the loading is finished or the level has appeared. This can be corrected by having the Game component disable itself before starting the load, then enabling itself after finishing loading. This time would also be where you would place a loading screen.
Awake gets invoked immediately when a scene is loaded, but the scene does not count as loaded yet. In Start and later Update calls, the scene is considered officially loaded.
They get into creating another level and go into unloading scenes to prevent their level scenes from piling up as the go back and forth loading new instances of them. They start by making sure to keep track of the current scene build index and updating that whenever they load a new scene (this is similar to the approach I took with my Tower Defense scene management).
They then use the SceneManager.UnloadSceneAsync method to unload the old level index, before loading the next scene. They make sure to yield on the unloading as well before loading the next scene. This is their example: IEnumerator LoadLevel (int levelBuildIndex) { enabled = false; if (loadedLevelBuildIndex > 0) { yield return SceneManager.UnloadSceneAsync(loadedLevelBuildIndex); } yield return SceneManager.LoadSceneAsync( levelBuildIndex, LoadSceneMode.Additive ); SceneManager.SetActiveScene( SceneManager.GetSceneByBuildIndex(levelBuildIndex) ); loadedLevelBuildIndex = levelBuildIndex; enabled = true; }
Applications
I could look into the disable/reenable method used in loading the level for my scene management issue. Since I have objects from different scenes that are both trying to reference something in the other at Start, maybe I could try to reenable the components that need to reference each other at the same time and have them create the references OnEnable.
Make sure Scene Management keeps track of the current build indices loaded and update this on scene loads. This can help when you finally need to unload the scenes before loading the next ones.