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.

Unity Scene Management – Creating Project SceneManager

March 16, 2019

Advancing Tower Defense Tutorial

Altering Scene Format

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

By: Egon Everest

Following in the footsteps of the FAR team from a Unite 2017 talk, I wanted to update my scene management in the tower defense tutorial to match that from the talk. To begin, I wanted to create a general Logic Scene to hold all of my managers and a Base Scene to hold general assets consistent between levels.

To start I needed to break up my current level scenes into their various parts since these will have all of the basics for every scene we are attempting to construct…

Since the Scene Fader will be used constantly as a way to transition between any and all scenes, this was included in the Logic scene that will persist the entire time the game is open. To make this accessible to all the other scenes and scripts however, I chose to have its script create a public static instance of itself. This was copied directly from the way the BuildManager was setup (creating a simple sort of singleton reference from the Brackeys tutorial):

public static SceneFader sceneFaderInstance;

private void Awake()
{
if (sceneFaderInstance != null)
{
Debug.Log(“More than one Scene Fader in scene”);
return;
}
sceneFaderInstance = this;
}

This way I could just go into all the scripts that had a public reference for the Scene Fader to drag into in the Inspector, and have the sceneFader variable to be set to SceneFader.sceneFaderInstance at Start instead of relying on the public reference.

Next I determined that I needed to figure out how to manage loading/unloading these various scenes now. Using basic Unity scene management, my scripts just load one scene at a time and unload all the current scenes. So even though I could test this by dragging in a Level scene and a Logic scene at the same time to make sure the new setup worked, everything would be back to loading one scene at a time as soon as I called a basic LoadScene method. To learn this, I went to look for a new tutorial.

I came across a Scene Manager tutorial dealing with loading/unloading objects in a more controlled manner that seemed perfect for what I was looking to do. There is a Scene Manager method called LoadSceneAsync which gives more options for how you load a scene. You give it a build index as well as a LoadSceneMode.

    LoadSceneMode has two options:

  • Single: the basic way Unity loads scenes where it also unloads all other currently loaded scenes
  • Additive: this just additionaly loads the inserted scene; this does not unload any other scenes

The Additive LoadSceneMode is the first step to accomplishing what I am looking for. At this time, I also decided it may make more sense to add some type of SceneManager object to my Logic scene to hold a SceneManager type script to deal with these functions. I did not want to run into any issues with Unity’s built in SceneManager so I just named all of this SceneManagerLite for now. This would use a similar singleton setup as referenced earlir for the BuildManager and the SceneFader.

Continuing along with this tutorial, this scene management method requires you to unload the currently loaded scene as well when needed. To do this, there was a simple Scene Manager method, UnloadScene. This is currently obsolete however but just needs replaced with UnloadSceneAsync. Similarly to LoadSceneAsync, this requires at minimum the build index of the scene you wish to unload.

So when I started to look into changing all of the scripts to fit within this new scene management scheme, I came across the realization that my Scene Fader is basically my Scene Manager Lite already. Every script that deals with changing scenes acceses the Scene Fader first to perform the “FadeOut” method created, which contains both the simple fade effect as well as the actual LoadScene method constantly being used that I wanted replaced. With this discovery, I renamed my Scene Fader to the Scene Manager Lite and made the necessary edits here. This simplified everything as now I could simply just change the FadeOut method to use the LoadSceneAsync and UnloadSceneAsync methods and it would apply to all the existing calls to that method.

I did end up needing to go through each method call and touching them up because the tutorial set the scene loading method up with a string input, and I wanted to change this to an int input to go with the build index. I figured the overall scene layout and build indices were pretty set at this point, so using build index instead of the scene names would be more consistent and easier to work with in the future as I added levels.

Outside Scene Management

Something important to note is that some of the scripts I wanted to update with the new Scene management would actually be referencing build indices for a scene other than themselves. Each of these are in the OverlayCanvas, which is an object I want to keep in the BaseScene, as all of these objects will remain consistent through every level. These are those scripts:

  • CompleteLevel: deals with functionality of UI menu when player successfully defeats a level
  • GameOver: deals with functionality of UI menu when player fails a level
  • PauseMenu: deals with functionality of UI menu when player pauses the game

This means that when they want to inform the SceneManagerLite of what scenes to load/unload, they will actually want to reference the build index of the LevelScene, not the index of their own scene (BaseScene).

Unite Europe 2017 – Multi-scene editing in Unity for FAR: Lone Sails

March 15, 2019

Unite Europe 2017

Multi-scene editing in Unity for FAR: Lone Sails

Youtube – Unite Europe 2017 – Multi-scene editing in Unity for FAR: Lone Sails

By: Goran Saric

I wanted to look into pushing the tower defense tutorial series I did from Brackeys into a more polished little game project, so one of the first upgrades I wanted to look into for the project was the suggestion about breaking up the scenes in a more efficient manner.

This was mentioned as the best way to set up the overall project if you wanted to make it significantly larger but keep it very easy to build upon. This is because there are a lot of objects in a given level currently that will persist between every level and right now they are just copied into every single level. If you make an edit to any of these objects, they need copied into every other scene again. This can be minimized by using prefabs much more extensively, but having a scene solely for these objects keeps everything much cleaner and easier to edit.

So searching for how to properly implement this idea of broken up scenes, I came across this Unite Europe 2017 talk where the makes of FAR: Lone Sails detail exactly how they approached the used of multi-scene editing in their game.

Before getting into their general additive scene setup, they mention how they broke down the main level content from a giant full story board type setup into equally sized level scenes. They then had two level scenes loaded at any given time to keep the memory usage down to a minimum, but ensure the next scene was ready when the player arrived.

The Scene Abstraction:

  • Logic Scene
    • Always loaded
    • Contains all relevant managers to keep game running
    • Examples: Scene Manager; Save Manager
  • Base Scene
    • All elements of game that are always present during gameplay and level independent
    • Examples: character(player); camera; their giant vehicle
  • Level Content 1 Scene
    • The rest of the level content that is unique for that area/level
  • Level Content 2 Scene
    • This is the same type as the previous scene
    • Just enforcing that the game has 2 level scenes loaded at any one time

They then detail some of their work flow with these level content scenes in the editor. Even though the game only ever has two scenes loaded at once, sometimes they had several level scenes all open at once to ensure the overall theme was consistent, for aligning geometry, etc. It is also noted the Editor play time gets faster by only having the scenes loaded that you need when testing. More broken up scenes also helps reduce merge conflicts.

Helper Tools

There were two main tools they mentioned being helpful for the designers to keep them organized: Highlight Scene Borders and Teleportation Keyboard Shortcuts.

Highlight Scene Borders: This tool just had a large colored plain at the ends of the scenes to help indicate where new scenes were starting/ending when having multiple open at once. This was especially helpful since they are dealing with more of a 2D platforming game world. This just helps ensure objects are placed in the correct scene, as well as helping determine camera frustrum angles.

Teleportation Keyboard Shortcuts: They had an issue where constantly during testing they would have to slide some of the major game components through the scenes to locate them. They discovered a much easier solution to this was to just have a keyboard shortcut that teleported these constantly moving pieces to the current mouse position. If done during the running of the game, this also has an added benefit that it doesn’t mess with the editor at all and will be reset to the proper location after testing.

Scene Collection

Unity doesn’t have a built in way to save scene hierarchies in the editor yet, but there are many tutorials online about creating your own editor tools to do this. Unity offers the corresponding API to do so, it just needs some work to be user friendly. They created a scriptable object that can save the constellation of all loaded scenes to be loaded again at a later time.

Cross-Scene References

Unity does not normally allow for cross scene references between objects within the Inspector. There are several ways to access an object from another scene though.

GameObject.Find: this is very slow and can be tricky to find the correct instance

Singleton/Statics: most programmers dislike using these, but they can be used for this purpose

Scriptable Objects: keep references of instances in a scriptable object and link them to your scripts

Zenject – Dependency Injection Framework: offers some built-in features to support reference injections over multiple scenes

For FAR, they didn’t need to do very many cross scene references in the way they setup the scenes. When they did need to do this however, they had a simple monobehavior component in the Base Scene which would access a reference in a static reference pool. This static field reference could then be influenced/updated by simple monobehavior components in the current Level Scene. This exposes some of the methods from that original component in the Base Scene. Their setup also helps keep the Level Designers from messing with scripts they shouldn’t be modifying.

Finally, if you want some really advanced features already setup for you, there is a tool you can buy on the Unity store. It’s titled “Advanced Multi-Scene: Cross-Scene References”. It has some nice features such as scene merging and cross scene Inspector referencing.