in C#, Tutorial, Unity

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

💖 Support this blog

This website exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.

Patreon Patreon_button
Twitter_logo

YouTube_logo
📧 Stay updated

You will be notified when a new tutorial is released!

📝 Licensing

You are free to use, adapt and build upon this tutorial for your own projects (even commercially) as long as you credit me.

You are not allowed to redistribute the content of this tutorial on other platforms, especially the parts that are only available on Patreon.

If the knowledge you have gained had a significant impact on your project, a mention in the credit would be very appreciated. ❤️🧔🏻

Write a Comment

Comment

17 Comments

  1. 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)

    • 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.

  2. 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?

  3. 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?

  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. 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
    }
    }

  6. 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 🙂

Webmentions

  • Implementing a Loading Bar in Unity - Alan Zucconi September 6, 2018

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