Using a multi-scene workflow in Unity/C#

Did you know that, in Unity, you can overlay multiple scenes to help with team collaboration and project organisation?

This article is also available on Medium.

The other day, I was reading up on some do’s and dont’s about collaborative dev in Unity and I stumbled across a really nice feature that I’d never really been aware of before: the ability to create a multi-scene workflow.

So – Unity 5 came with its lot of changes, among which a complete refactor of the scene management system. Whereas the old versions used methods like Application.LoadLevel(), you now have a package dedicated to loading, unloading or listing scenes: the UnityEngine.SceneManagement module.

With this new system, it’s way easier to handle your scenes and, in particular, load or unload them one by one… and on top of one other!

That’s the idea of a multi-scene workflow: you overlay several scenes at the same time and combine them into a final “complete scene”

But, wait, why is it that interesting? What are the benefits of multi-scene editing/programming? And what tools do we have to do it in Unity?

Let’s take a look at this!

Why use a multi-scene workflow?

Below are some examples of really cool things we can do thanks to a multi-scene workflows: those are some use cases that I have personally tried out but there are of course plenty of others! 😉

For collaborative dev

One of the most prominent advantages of a multi-scene workflow is that, because you split your project in small logical chunks, you get a better overall organisation and less risk of conflicts when you merge commits from multiple team members.

Multi-scene development is great for collaboration because, by definition, it allows each artist/programmer to work on their own feature without impacting the others. It reduces the amount of data that is shared which, in turn, reduces dependencies and therefore frictions.

For example, you can split up your Game scene in several parts: the C# scripting logic on one hand (which is done by the programmer on your team), the UI (done by your frontend dev/UI expert) and the asset/3D level contents (done by your 3D artist and level designer).

Here is a very basic example where I have 4 scenes open together:

  • the Core scene handles some initial loading and booting of session-scoped variables
  • the Game scene contains the logic for my game level
  • the Game UI scene has the UI Canvas shown in the middle of the scene and its own manager dedicated to handling the UI stuff
  • finally, the 3DScene contains the actual 3D objects in my scene: the Cube, the Sphere and the Cylinder

You see that Unity has specific hierarchy dividers for scenes: they allow you to quickly see which scene your objects belong to and to access various options like setting the scene as active, deleting it from the hierarchy or unloading it (which only removes the contents but keeps it in the hierarchy):

This gives us a clear visualisation of the multi-scene workflow core idea: it’s all about adding and subtracting scenes to the current context! When you load up a new scene on top of another, we say that you load it in “additive” mode (instead of the default “single” mode).

To keep some data throughout the entire session

Another great benefit of multi-scenes is that they let you keep some objects “in the background” with their current configuration or data. So you don’t need to revert back to the DontDestroyOnLoad technique every time you want to preserve some configuration/data asset: instead, you can put it in a specific scene that is loaded at the very beginning and then stays there throughout the play session!

Note: that’s what my “Core” scene is for in the above example 😉

To make smooth(er) scene transitions

In the same vein, multi-scenes can also help with smoothing out your scene transitions.

Of course, there are ways of preventing too-harsh jumps even with a single-scene workflow: I did an article a while ago about how we can use Unity animations to create fade-ins and fade-outs for our scene transitions (both on the UI and the background music).

But the nice thing with multi-scenes is that, since you can keep some objects alive in the background (and in particular as you load your new scene), you don’t have to “interrupt” anything! For example, your audio source can continue to play in its own “AudioCore” scene, and this way the player won’t even feel like they transitioned from one scene to another 😉

To avoid large scenes

Alternatively, splitting up your scenes can be a neat way of avoiding having huge un-manageable scenes in your project. Even if your logic and UI are pretty minimal, as you start adding up props and objects, you might slowly start to crowd your scene with a lot of info.

If your 3D scene is too big, you can perhaps divide it into smaller rooms, or logical “layers” (like the terrain, the buildings, the props and so on). This way, you get more condensed chunks of your level to focus on that you can quickly re-assemble just by opening the scenes together.

How to make a multi-scene workflow?

Thanks to Unity 5 new scene management system, having multiple scenes loaded at the same time is now pretty straight-forward, both in the editor or at runtime via scripting.

Handling multiple scenes in the editor

Before the update, you couldn’t really stack scenes on top of each other in the editor. There were some addons and store assets to help with you that, but it’s always a pain to download and import all these dependencies – it’s a good thing Unity now handles it natively! 😉

With Unity 5+, simply drag and drop a scene to your hierarchy panel and it will automatically be open in additive mode. This way, you are essentially loading up a new set of objects, scripts, cameras, lights, etc. (everything that’s in your second scene) and stacking them with the ones already present.

Or you can also right-click on your scene asset and click “Open Scene Additive”:

As mentioned before, you can then remove them or unload them from the hierarchy using the scene header menu.

Something that can be quite useful if you have a complex assembly of scenes and you don’t want to rebuild it every time you re-open the Unity editor is to save this scene setup in your project. To do this, you can serialise the results from the EditorSceneManager.GetSceneManagerSetup() into a Scriptable Object and reload it later on with the EditorSceneManager.RestoreSceneManagerSetup() method.

Handling multiple scenes with C# scripting

To handle your scenes in C#, you’ll first want to import the UnityEngine.SceneManagement package.

Then, you’ll be able to get the list of the scenes in your project build settings, or access a specific scene by name/path/build index for example thanks to the SceneManager class:

Of course, you can also load a scene with the well-named method LoadScene() (in “single” or “additive” mode).

Something important to note though is that this function is synchronous: this means that it will block the rest of your program until it’s done. While this probably is not a problem for small scenes, it could have your game “freeze” for a short while if you’re loading a larger scene…

Similarly, you could use UnloadScene(), but it’s not the most efficient way to change your current scene setup.

In fact, the API offers you the asynchronous equivalents: LoadLevelAsync() and UnloadLevelAsync().

Using the async versions just means calling the right function in your script. However, just calling these methods “as is” will run these async processes in a “fire-and-forget” way: you ask Unity to load/unload the scene and then, hopefully, all will turn out well and all the async processes will complete properly.

But as usual when doing async programming, it can be interesting to check that your processes did complete. This is easy enough to do with the SceneManagement API: when you call the async versions, you’ll get back an AsyncOperation variable that has a completed event you can listen and react to.

So, for example, you may want to wait for your “Main Menu” scene to finish loading before calling the initialisation logic on its manager:

You can also do it in coroutines using a yield statement and checking for the isDone property of the AsyncOperation:

The API has various other utilities to move objects between the currently loaded scenes (which can be useful to implement more efficient object pooling…), setting which one is active, merging scenes together, etc.

Bonus: doing editor scripting for multi-scenes

By the way, you can even do some custom editors and windows that interact with this scene system thanks to the Scene struct, the EditorSceneManager API and the SceneSetup utility class. You can even create, update or destroy prefabs across scenes on the fly with the PrefabUtility class!

A quick note on scene-specific settings

If you start using a multi-scene workflow, chances are that, pretty soon, you’ll get to loading a new scene via script at runtime and… find yourself in a dark and almost pitch-black level.

That’s because some settings, like lighting, are scene-specific.

So let’s say that, like me, you decided to have some global scene template in a “Game” scene and then only keep the 3D objects in your “Level” scene. This “Level” scene doesn’t have any light: the Directional light is in the template “Game” scene.

Ok, and?

Well: when you load up a scene additively, it is not able to dynamically pick up the lights from another already loaded scene. This means that you have to bake your lighting before hand, thanks to the Lighting panel (found in Window > Rendering > Lighting).

To do this, open your non-lit scene and the scene that contains the light (in my case, the “Level” and “Game” scenes) and make sure your active scene is the one with the objects and not the light (because that’s the scene you want to bake the lighting for). Then, open the Lighting panel, make sure “Auto Generate” is toggled off and click on “Generate Lighting”:

This will generate a new folder next to your scene asset with the same name that contains a Lighting Data asset and Reflection Probe data with your baked lighting:

Note that you can also do it via scripting using the Lightmapping.BakeMultipleScenes() function.

Similarly, NavMeshes and occlusion culling are scene-specific; so make sure to bake them properly too (more info in the Unity docs)! 🙂

Conclusion

I’m still pretty new to using multi-scene workflows but I already find it amazingly refreshing to shift my mindframe and learn about this new way of developing!

Designing a game with this workflow requires you to better structure your project which, ultimately, is always a good thing. And thanks to Unity’s new tools, it’s now really easy and intuitive to do – we don’t have to dive into the asset store and download additional packages anymore!

What about you? Did you know of this feature? Do you use it often in your own projects, and have you seen a difference with the “classical” single-scene workflow? Feel free to react in the comments!

Leave a Reply

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