Scene Management in Unity 5

This tutorial shows how the manage scenes and levels in Unity 5.3, with the introduction of UnityEngine.SceneManagement.

Introduction

It took Unity more than 5 versions, but it seems that finally it’s converging towards a uniform and sensible set of APIs to manage scenes. In its initial version, Unity scenes were referred in the code as levels. It came pretty quickly the realisation that scenes do not necessarily define “different” levels; this forced the introduction of the term “scene” directly into the API. From a logical point of view, levels are addressed with their build number (the order in which they are included into the Build Settings); scenes are referred with the name of the asset that contains them. Neither of those, however, are reliable unique identifiers for a scene. Moving scenes in the Build Settings shifts all of their indices. Similarly, you can have two scenes with the same name, stored in two different folders. All of these aspects have fuelled a lot of confusion, which Unity 5.3 is trying to end.

What’s new in Unity 5.3

Unity 5.2 allowed scenes to be referred with either their build index or their asset name. In Unity 5.3 there is an additional way, which is the Scene struct (documentation). It’s a wrapper for common methods and variables such as buildIndex, name and path.

Accessing the SceneManager class

In order to use these features of Unity 5.3, you need to include in your C# files this package SceneManagement:

using UnityEngine.SceneManagement;

This gives access to the SceneManager class (documentation), which replaces the obsolete functionalities of Application.

Retrieving the current scene

One of the main reasons why Unity changed its naming convention from “level” to “scenes” is mainly because you can load multiple scenes at the same time. This breaks the concept of “current level”, which has been replaced with “active scene”. In the vast majority of cases, the active scene is the last one that has been loaded. You can query it at any given time using GetActiveScene:

// 5.3
Scene scene = SceneManager.GetActiveScene();
Debug.Log(scene.name);
Debug.Log(scene.buildIndex);

// 5.2
Debug.Log(Application.loadedLevelName);
Debug.Log(Application.loadedLevel);

You can test against a specific scene simply by using:

if (SceneManager.GetActiveScene().name == "sceneName")
{
    // ...
}

Alternatively you can use the == operator to compare two scenes:

if (SceneManager.GetActiveScene() == scene)
{
    // ...
}

Unity has overridden the behaviour of == so that it works as expected.

Loading a single scene

Like in Unity 5.2, scenes can be loaded using their build index or their name.

// 5.3
SceneManager.LoadScene(4)
SceneManager.LoadScene("scene4")
SceneManager.LoadScene("path/to/scene/file/scene4")

// 5.2
Application.LoadLevel(4);
Application.LoadLevel("scene4");

Weirdly, the new LoadScene doesn’t accept Scene structs. When a new scene in loaded with this method, the previous one is unloaded (all its objects destroyed). The game will freeze for a little bit while the new scene is being loaded and activated.

Reloading a scene

There is no single function to reload the current scene. The best way you can do is to get the build index of the current scene, and pass it to LoadScene:

SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); // 5.3
Application.LoadLevel(Application.loadedLevelName); // 5.2

This only works as intended if you have a single active scenes. If you have loaded multiple scenes, this will only reload the last active one.

Issues

  • Ghost scenes. Every time you load a new level with SceneManager.LoadScene, Unity flushes all the scenes that have been previously loaded. Only game objects that invoked DontDestroyOnLoad (documentation) survive the process. This means that you can potentially use LoadScene, yet having all the previous scenes still loaded. Despite this, Unity will fail to realise that those scenes are actually still alive.
  • Scene struct. The Scene struct does not update itself. If something in the scene has changed, its isDirty flag won’t suddenly become true. If you want to query the state of the scene, you should not store the structure; use GetActiveScene instead.

Conclusion

Become a Patron!

This tutorial shows some of the features introduced by Unity 5.3 to manage scenes. All the changes that Unity is making to its engine are bringing closer to a more clean and reliable flow to handle the dynamic loading of content. There are few aspects, however, that are still quite messy. And is unlikely Unity is going to get rid of them any time soon.

The next part of this tutorial will explore how to load scenes asynchronously, implementing a loading bar.

Other resources

Comments

18 responses to “Scene Management in Unity 5”

  1. so clean & succinct; thanks for the overview!

    1. You’re welcome! <3

  2. Hi Alan!

    I stumbled across your article while searching for a way to make a seemless streaming-world in Unity, so is there any chance that you publish part 3 of this article anytime soon as this seems to give me the answers/tricks I am looking for!

    Have a nice day 🙂

    1. Hey!
      Sorry for the delay, unfortunately, there’s no time planned for the third part! Might be later 2018! 🙁

  3. Hey, Alan.
    I have MonoBehaviour script and it uses tag [ExecuteInEditMode]. I want to check any change on the unity scene. Can I just do as following?
    void Update(){
    if (.isDirty){
    // do things
    }
    }

    1. Hey!
      I’m not sure exactly what you are trying to achieve!
      Can you give me an example?

  4. Thanks for your tutorial. The second issue(Scene struct does not update itself) is not actually an issue. This is how a struct is supposed to work. Struct is a value type, not a reference type. So if a state inside SceneManager changes, the struct you previously get from it, is not going to receive that change.

  5. So I implemented this on a game I’m building, problem is that unity crashes and never gets to finish the job. I suppose it’s something to do with that while loop in there. Any ideas?

    1. It’s very hard to know what’s wrong with your setup without seeing it! 🙁

  6. Very useful was struggling to understand this.

  7. Richard DeTaranto avatar
    Richard DeTaranto

    I have a program with a lot of buttons to open new screens. It stopped working with the old format. Can’t as of yet get them to work. I was using the string variable to open the new screen when a button is clicked. Anyone figure this out yet?

  8. Very well explained. Thanks!

  9. […] Part 1. Scene Management in Unity 5 […]

  10. Thank you for this post!
    I was not aware of the SceneManagement package before. 🙈

    What are your thoughts on other solutions to organize and load content?
    (e.g. putting the all GameObject of one level into a Prefab and activating it after is has been instantiated to avoid working with multiple scenes all together)

    1. Hey Ruben!

      I think it all depends on the complexity of your game and project. The concept of “level” is getting a little bit old. Lot of games just have a continuous gameplay experience. If you have A LOT of stuff to load, hiding it into deactivated prefabs is not a good idea. They won’t be executed, but they’ll still be in memory and cause massive delays when loading the game.

  11. I love your Unity tutorials. Please keep writing them 🙂

    1. Thank you so much! 😀

  12. Good work.
    Grazie 😉

Leave a Reply

Your email address will not be published. Required fields are marked *