Unity Scene Management – Timed Loading and Build Indexing

March 17, 2019

Advancing Tower Defense Tutorial

Altering Scene Format

Youtube – Unity- Scene Manager and Keeping Objects when Loading/Unloading

By: Egon Everest

Myriad Games Studio – HOW TO USE THE UNITY SCENEMANAGER

I continued to updated the scene management of my tower defense tutorial. One of the biggest additions was that I did decide to create a single location for all of the OverlayCanvas button methods I wanted to use. This was just a script called the OverlayCanvasManager that I added to the GameManager object. This just made sense with how often different UI buttons would do similar actions, like going to the menu. This script then became the sole reference point between the Base Scene and the SceneManagerLite object in the Logic Scene.

As I was working with the references between the OverlayCanvasManager and the SceneManagerLite, it became more clear that the SceneManagerLite should still directly handle all of the actual scene management. The OverlayCanvasManager would then become the reference to use for the OverlayCanvas buttons (since I needed an object to drag in the inspector for the buttons, but that has to be within the same scene), and would basically tell the SceneManagerLite what to do based on what buttons were pressed.

Having the SceneManagerLite be directly responsible for everything scene related also made more sense for holding variable values. Now instead of several scripts needing to have information about different build indices for scenes like the Main Menu or the Current Level, this information could just be kept in this one location and updated much more easily.

PROBLEMS

The first main issue I was having with this setup is that the loading times of the scenes appear to be off. Sometimes the scenes would overlap because the next one would be finished loading before the previous was unloaded. I need to be able to perform some sort of check to do one after the other, or have some simple UI setup to block the process until everything is properly loaded.

The next big issue I had was debugging all of these new scene transitions. I updated every instance of transitioning between scenes, so making sure these all went to the right place took some time. I also had an issue where using the Menu or Retry methods from the OverlayCanvasManager (which tied in to the Menu or RetryLevel methods of the SceneManagerLite) was unloading my Logic Scene and just loading unintended scenes. My debugging led me to believe that this was an issue with the data transfer from the Current Level Scene to the SceneManagerLite.

As part of having all of the build index information be in the SceneManagerLite, I needed a way for it to know the build index of whatever the Current Level Scene was. I could not get this information from anything in the Base Scene, as this has its own build index. To solve this, I created a very simple object in the Level Scene, SceneInformation, that would have a script, SceneInformation, that set the currentLevelBuildIndex value in the SceneManagerLite to this object’s scene build index at Start. This was not happening however, as when I Debugged the Menu method, it was showing me that the currentLevelBuildIndex was 0. This showed me why the Logic Scene was being unloaded, and that this value was not being updated, as it was remaining at its default of 0.

I think what is causing the issue is that I am using the SceneManager GetActiveScene method, and this may not be doing what I thought it did. This is the suggested method to get your scene’s build index normally, but this may not be the case when you have multiple. If there are multiple, the ActiveScene may not necessarily be the one the object is in.

NOTES

From the problems I ran into, I had to do more research on the SceneManager for Unity, specifically what an active scene meant. The active scene is the scene that objects are created in whenever objects are instantiated. So if you have multiple scenes open, instantiated objects do NOT automatically appear in the scene of the script instantiating them. Everything from any scene that is instantiated will go to the one designated active scene. When you add scenese additively, the active scene does not change.

With this information, I updated my SceneManagerLite so its transition would now set the newly loaded scene as the active scene as well. This makes sense as I don’t want the Logic Scene to be where objects are instantiated, so immediately making newly loaded scenes active removes it from being the active scene. This also helped my issue with finding the build index of the Current Level scene. It’s obvious I want that to be the active scene when the player is in game, and now using the SceneManager method GetActiveScene will properly return the build index of the Current Level scene itself.

This ideally works in theory, but I ran into a new issue where I could not set the newly loaded scenes as active because Unity did not think they were loaded yet. I got the error message:

ArgumentException: SceneManager.SetActiveScene failed; scene ‘Level01’ is not loaded and therefore not set to active

This seemed to be caused by an issue where loading scenes takes a bit of time, so it wasn’t loaded when Unity was calling to set the scene to active. I investigated a solution with IEnumerators for this.

Looking into IEnumerators to deal with the timing issue for setting my active scene led me to this great scene management source by Myriad Games Studio. They had the script written out for a clean asynchronous scene loader. I used this as the basic foundation for a Scene Loader of my own. I needed to tweak it some for my additive scene needs, and I decided to not use the Singleton class they used for now just to keep it simpler. This now set the active scene specifically after loading the scene, so this solved that issue! Now however, I found out it was loading the wrong scene index.

The reason I was loading the wrong build index now with my Menu and Retry methods is because the SceneInformation script that was getting that active build index at Start was returning a value of 1. I had it return the active scene build index in Update to see if it ever changed, and it would correctly change to 4 on the second frame. Not only did it return 1 in Start, it also returned 1 on the very first run of Update. Then every other instance was the proper 4. This has something to do with the fact that when I load a scene I set it active as well, so there is some delay between creating the Base Scene and creating the Level Scene where Start functions in the Level Scene run before the Level Scene is set to active.

The fact that I was loading in to the proper level though gave me an idea to fix it. The fact that the right level was initially loading meant somewhere had the build index correct, and that was the Level Selector. With this information, I simply looked into the SceneManagerLite LevelSelect method and set the currentLevelBuildIndex in there to be the value of the index that is passed in. This ensures that whatever index is loaded for the level will also be the index used for the other methods, which actually makes sense.