This is the fourth tutorial in a series about Object Management. It's about putting objects in their own scene, working with multiple scenes at once, plus loading and unloading scenes.

This tutorial is made with Unity 2017.4.4f1.

Check whether the shape is active, via the activeSelf property of its game object. If it is not active, then we have a shape awaiting reuse and must add it to the appropriate pool list.

Next, loop through all the objects and grab their shape component. As this is the factory scene, it should only contain shapes, so we always get a component. A null reference error after this point would indicate a problem elsewhere.

Yes. There's also a variant that uses a list parameter, which can be used to avoid the temporary array. But we're in the editor after a recompilation, so don't really need to worry about memory efficiency here.

The second and slightly less obvious problem is that shape instances that were inactive before a recompilation never get reused. That's because we've lost the lists that kept track of them. We can solve this by repopulating the lists. First, retrieve an array containing all root game objects of the pool scene, via the Scene.GetRootGameObjects method.

This works, but we only really need to do this while working in the Unity editor, not in builds. We can check if we're in the editor via the Application.isEditor property.

This doesn't appear to work. That's because Scene is a struct, not a direct reference to the actual scene. As it is not serializable, a recompilation resets the struct to its default values, which indicates an unloaded scene. We have to request a fresh connection, via the SceneManager.GetSceneByName method.

The first obvious problem is that we try to create the pool scene again, which will fail because a scene with that name already exists. We can guard against this by checking whether the pool scene is already loaded, via the Scene.isLoaded property. If so, we abort before creating the scene.

That will make Unity save the pools as part of the asset, persisting it between editor play sessions and including it in builds. That is not what we want.

While Unity serializes the private fields of MonoBehaviour types when compiling, it doesn't do this for ScriptableObject types. This means that the list of pools is lost after a recompilation. The result of this is that CreatePools will get invoked again after a recompilation.

The factory is working fine, at least in a build or as long as we remain in play mode. Unfortunately, a recompilation while in play mode messes up our recycling and pool scene.

From now on, shapes are neatly put in the Shape Factory scene, which you can collapse in the hierarchy window, or leave open when you want to take a look.

When a game object is instantiated, it gets added to the active scene. In our case, the active scene is Scene, the only persistent scene in our project. It is possible to change the active scene, but we don't want the factory to mess with scenes. Instead, we can migrate our shapes to the pool scene after creating them, by invoking SceneManager.MoveGameObjectToScene , with the game object and scene as arguments.

Now a Shape Factory scene appears the first time we create a shape while in play mode, although the shapes aren't put into it yet. The scene vanishes when we stop playing.

We only need a scene when recycling is enabled. When not recycling, managing the instances can be left up to whoever requested them. So we only need to create a scene when we need pools. Thus, at the end of CreatePools invoke SceneManager.CreateScene to make a new scene and keep track of it. The scene needs a name, for which we can simply use the name of the factory. If you use multiple factories, they'd all get their own scene, so make sure to give each a unique name.

We'll create a pool scene to contain all shape instances that can be recycled. All the factory's shapes go into this pool and should never be removed from it. We can use a Scene field to keep track of this pool scene.

ShapeFactory is responsible for creating, destroying, and recycling shapes, so it should also be responsible for the scene that holds them. To work directly with scenes, it needs to access code from the UnityEngine.SceneManagement namespace, so use it in the ShapeFactory class file.

We want a dedicated scene for containing shape instances. As shape instances only exist in play mode, the scene also only needs to exist while we're in play mode. So we're going to create one via code, not via the editor.

The second option is to put all shapes in a separate scene. They remain root objects without parents, but become part of an extra scene, which can be collapsed in the hierarchy window. Scenes don't care about the state of their objects, so this doesn't slow the game down. This is the option that we'll use.

The first option is to create a root object and make all shapes children of that object. Then we can collapse the root object. Unfortunately, this can negatively impact our game's performance when shapes are changed. Whenever an object's active or transform state changes, all its parent objects are notified of this change. So it's best to avoid making objects children of another object when this isn't strictly necessary.

Potential editor slowdowns can be prevented by collapsing the scene hierarchy, or by removing the hierarchy window, but then we can't see the objects anymore. Ideally, we could collapse all shape instances into a single entry in the hierarchy window, while everything else remains visible. There are two ways to do this.

When many shapes are instantiated while in play mode, the scene rapidly fills with objects and the hierarchy window can get quite cluttered. This can make it hard to find a specific object, and can also slow down the editor.

Level 1

Scenes aren't only useful for grouping objects in play mode. Often, projects are partitioned into multiple scenes. The most obvious configuration is one scene per game level. But a game usually has objects that do not belong to a single level, but to the entire game. Instead of putting a copy of those objects in every scene, they can be put in their own scene too. This allows you to break up your project into multiple scenes, but requires multiple scenes to be open at the same time while editing.

Multi-Scene Editing We're going to split our game into two scenes. Our current scene is the main scene, so rename it to Main Scene. Then create a second scene via File / New Scene, named Level 1. This new scene represents the first level of our game. Main scene and Level 1 scene. Now open the Level 1 scene, while keeping the Main Scene open as well. This is done by dragging the scene from the project window into the hierarchy window. The Level 1 scene will get added below Main Scene, just like our pool scene appears in play mode. Main Scene is displayed with bold text because it's still the active scene. If you'd enter play mode now, you end up with three scenes: main, level, and factory pool. Two scenes at the same time. The idea is that the main scene contains everything needed to run the game, no matter which level we're playing. In our case, that's the main camera, the Game object, storage, canvas, and event system. But we'll make the lighting depend on the level. So delete the light from Main Scene and the camera from Level 1. One camera and one light.

Scene Lighting The only thing that we changed is that we put the light in a separate scene, which is also open. The game should function just as before. However, there is a difference. It turns out that the environmental lighting has become very dark. Environmental lighting too dark. Besides being a collection of game objects, scenes also have their own lighting settings. The environmental lighting changed because the main scene no longer has a light in it, and as a result its environmental lighting has gone dark. We get this result because the lighting settings of the active scene is used. The level scene has a light in it, with matching environmental lighting. So to fix the lighting, we have to make Level 1 the active scene. This can be done via the Set Active Scene option in the dropdown menu of each scene in the hierarchy window.

Level 1 as the active scene.

Including Multiple Scenes in Builds With Level 1 as the active scene our game works as expected, at least in the editor. To make it work correct in a build as well we have to make sure that both scenes are included. Go to File / Build Settings… and make sure both scenes are added, either by clicking Add Open Scenes or by dragging them into the Scenes In Build list. Make sure that Main Scene has index 0 and Level 1 has index 1. Building both scenes. From now on, both scenes get added to builds, even when they are not open when building. You could unload a scene, via the Unload Scene option in its dropdown menu. That keeps it in the hierarchy window, but disabled. Level 1 not loaded. You can also use the Remove Scene option. That unloads and removes it from the hierarchy window. It doesn't delete it from the project. Level 1 removed from hierarchy.

Loading a Scene Even though both scenes are included in builds, only the first scene—with index 0—gets loaded when the game build is run. This is the same as having only the main scene open in the editor when entering play mode. To make sure that both levels are loaded, we have to manually load Level 1. Add a LoadLevel method to Game. In it, invoke SceneManager.LoadScene with the name of our level as an argument. using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class Game : PersistableObject { … void LoadLevel () { SceneManager.LoadScene("Level 1"); } … } Our game doesn't have a splash screen, logo intro, or main menu, so immediately load the level when it awakens. void Awake () { shapes = new List<Shape>(); LoadLevel(); } This doesn't have the desired effect. Unity unloads all currently open scenes, then loads the requested scene. The result is that we end up with nothing but the light object. This is equivalent to double-clicking on a scene in the editor. What we want is to load the level scene in addition to what's already loaded, like we did earlier in the editor. This can be done by providing LoadSceneMode.Additive as an additional argument to SceneManager.LoadScene . void LoadLevel () { SceneManager.LoadScene("Level 1" , LoadSceneMode.Additive ); } Give it a try in the editor, without having Level 1 loaded. It works, but unfortunately the environmental lighting is again not correct, though this time it is harder to spot. It is slightly too dark. Incorrect environmental lighting. Once again, we have to make sure that Level 1 is the active scene, this time via code. It's done by invoking SceneManager.SetActiveScene , with a Scene parameter. We can get the required scene data via SceneManager.GetSceneByName . void LoadLevel () { SceneManager.LoadScene("Level 1", LoadSceneMode.Additive); SceneManager.SetActiveScene(SceneManager.GetSceneByName("Level 1")); } Unfortunately, this results in an error. SceneManager.SetActiveScene only works for scenes that are loaded, which it apparently isn't, even though we just invoked LoadScene . That's because loading scenes takes some time. The scene is only fully loaded on the next frame.

Waiting a Frame Because a loaded scene doesn't become fully loaded immediately, we have to wait until the next frame before we make it the active scene. The easiest way to do this is by turning LoadLevel into a coroutine. Then we can yield once between invoking LoadScene and SetActiveScene , adding a delay of a single frame. using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class Game : PersistableObject { … void Awake () { shapes = new List<Shape>(); StartCoroutine( LoadLevel() ) ; } … IEnumerator LoadLevel () { SceneManager.LoadScene("Level 1", LoadSceneMode.Additive); yield return null; SceneManager.SetActiveScene(SceneManager.GetSceneByName("Level 1")); } … }

Baking Lighting Although Level 1 now correctly becomes the active scene, we still do not get correct environmental lighting. At least, not in the editor. Builds are fine, because all lighting gets properly included. But in the editor automatically generated lighting data doesn't work correctly when loading scenes in play mode. To ensure correct lighting in the editor, we have to turn off the Auto Generate option, found at the bottom of the lighting settings, opened via Window / Lighting / Settings or Window / Rendering / Lighting Settings depending on the Unity version. Manual lighting generation. Open the Level 1 scene, make sure that it is the active scene, and click Generate Lighting. Unity will bake the lighting data and save it in a folder next to the scene asset. Scene lighting data for level 1. These settings are per scene. You only have to manually bake Level 1. We don't use the lighting data of the main scene, so you can leave that one in auto-generate mode.

Asynchronous Loading How long it takes to load a scene depends on how much it contains. In our case, it's a single light, so it loads very quickly. But in general it can take a while to load, which would freeze the game until finished. To prevent this, it is possible to load scenes asynchronously, via SceneManager.LoadSceneAsync . This begins the process of loading a scene and returns an AsyncOperation object reference, which can be used to check whether the scene has finished loading. Alternatively, it can be used to yield in a coroutine. Let's do that, instead of yielding exactly one frame. 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")); } Now our game doesn't freeze when loading a level. This means that it's possible that our game's Update method gets invoked an arbitrary amount of times before the level is loaded and has become the active scene. This is a problem, because it makes it possible for the player to issue commands before the level has been loaded. To prevent this, the Game component must disable itself before beginning the loading process, and enable itself again after loading has finished. IEnumerator LoadLevel () { enabled = false; yield return SceneManager.LoadSceneAsync( "Level 1", LoadSceneMode.Additive ); SceneManager.SetActiveScene(SceneManager.GetSceneByName("Level 1")); enabled = true; } In a more complex game, you would also show and hide a loading screen at these points.