Search Unity

Managing multiple Scenes in Unity can be a challenge, and improving this workflow is crucial for both the performance of your game and the productivity of your team. Here, we share some tips for setting up your Scene workflows in ways that scale for bigger projects. 

Most games involve multiple levels, and levels often contain more than one Scene. In games where Scenes are relatively small, you can break them into different sections using Prefabs. However, to enable or instantiate them during the game you need to reference all these Prefabs. That means that as your game gets bigger and as those references take up more space in memory, it becomes more efficient to use Scenes.

You can break down your levels into one or multiple Unity Scenes. Finding the optimal way to manage them all becomes key. You can open multiple Scenes in the Editor and at runtime using Multi-Scene editing. Splitting levels into multiple Scenes also has the advantage of making teamwork easier as it avoids merge conflicts in collaboration tools such as Git, SVN, Unity Collaborate and the like.

Managing multiple Scenes to compose a level

In the video below, we show how to load a level more efficiently by breaking the game logic and the different parts of the level into several distinct Unity Scenes. Then, using Additive Scene-loading mode when loading these Scenes, we load and unload the needed parts alongside the game logic, which is persistent. We use Prefabs to act as “anchors” for the Scenes, which also offers a lot of flexibility when working in a team, as every Scene represents a part of the level and can be edited separately.

You can still load these Scenes while in Edit Mode and press Play at any time, so that you can visualize them all together when creating the level design. 

We show two different methods to load those Scenes. The first one is distance-based, which is well suited for non-interior levels like an open world. This technique is also useful for some visual effects (like fog, for instance) to hide the loading and unloading process.

The second technique uses a Trigger to check which Scenes to load, which is more efficient when working with interiors.

Now that everything is managed inside the level, you can add a layer on top of it to better manage the levels.  

Managing multiple levels within a game using ScriptableObjects

We want to keep track of the different Scenes for each level as well as all the levels during the entire duration of the gameplay. One possible way of doing this is to use static variables and the singleton pattern in your MonoBehaviour scripts, but there are a few problems with this solution. Using the singleton pattern allows rigid connections between your systems, so it is not strictly modular. The systems can’t exist separately and will always depend on each other. 

Another issue involves the use of static variables. Since you can’t see them in the Inspector, you need to change the code to set them, making it harder for artists or level designers to test the game easily. When you need data to be shared between the different Scenes, you use static variables combined with DontDestroyOnLoad, but the latter should be avoided whenever it is possible.

To store information about the different Scenes, you can use ScriptableObject, which is a serializable class mainly used to store data. Unlike MonoBehaviour scripts, which are used as components attached to GameObjects, ScriptableObjects are not attached to any GameObjects and thus can be shared between the different Scenes of the whole project. 

You want to be able to use this structure for levels but also for menu Scenes in your game. To do so, create a GameScene class that contains the different common properties between levels and menus.

Notice that the class inherits from ScriptableObject and not MonoBehaviour. You can add as many properties as you need for your game. After this step, you can create Level and Menu classes that both inherit from the GameScene class that was just created – so they are also ScriptableObjects.

Adding the CreateAssetMenu attribute at the top lets you create a new level from the Assets menu in Unity. You can do the same for the Menu class. You can also include an enum to be able to choose the menu type from the Inspector.

Now that you can create levels and menus, let’s add a database that lists the levels and menus for easy reference. You can also add an index to track the current level of the player. Then, you can add methods to load a new game (in this case the first level will be loaded), to replay the current level, and for going to the next level. Note that only the index changes between these three methods, so you can create a method that loads the level with an index to use it multiple times.

There are also methods for the menus, and you can use the enum type that you created before to load the specific menu you want –  just make sure that the order in the enum and the order in the list of menus is the same.

Now you can finally create a level, menu or database ScriptableObject from the Assets menu by right-clicking in the Project window.

From there, just keep adding the levels and menus you need, adjusting the settings, and then adding them to the Scenes database. The example below shows you what Level1, MainMenu and Scenes Data look like.

It’s time to call those methods. In this example, the Next Level Button on the user interface (UI) that appears when a player reaches the end of the level calls the NextLevel method. To attach the method to the button, click the plus button of the On Click event of the Button component to add a new event, then drag and drop the Scenes Data ScriptableObject into the object field and choose the NextLevel method from ScenesData, as shown below.

Now you can go through the same process for the other buttons – to replay the level or go to the main menu, and so on. You can also reference the ScriptableObject from any other script to access the different properties, like the AudioClip for the background music or the post-processing profile, and use them in the level.

Tips for error-proofing your processes

  • Minimizing loading/unloading

In the ScenePartLoader script shown in the video, you can see that a player can keep entering and leaving the collider multiple times, triggering the repeated loading and unloading of a Scene. To avoid this, you can add a coroutine before calling the loading and unloading methods of the Scene in the script, and stop the coroutine if the player leaves the trigger. 

  • Naming conventions

Another general tip is to use solid naming conventions in the project. The team should agree beforehand on how to name the different types of assets – from scripts and Scenes to materials and other things in the project. This will make it easier not only for you but also for your teammates to work on the project and to maintain it. This is always a good idea, but it’s crucial for Scene management with ScriptableObjects in this particular case. Our example used a straightforward approach based on the Scene name, but there are many different solutions that rely less on the scene name. You should avoid the string-based approach because if you rename a Unity Scene in a given context, in another part of the game that Scene will not load.

  • Custom tooling

One way to avoid the name dependency game-wide is to set up your script to reference Scenes as Object type. This allows you to drag and drop a Scene asset in an Inspector and then safely get its name in a script. However, since it’s an Editor class, you don’t have access to the AssetDatabase class at runtime, so you need to combine both pieces of data for a solution that works in the Editor, prevents human error, and still works at runtime. You can refer to the ISerializationCallbackReceiver interface for an example of how to implement an object which, upon serialization, can extract the string path from the Scene asset and store it to be used at runtime.

In addition, you might also create a custom Inspector to make it easier to quickly add Scenes to the Build Settings using buttons, instead of having to add them manually through that menu and having to keep them in sync.

As an example of this type of tool, check out this great open source implementation by developer JohannesMP (this is not an official Unity resource).

Let us know what you think

This post shows just one way that ScriptableObjects can enhance your workflow when working with multiple Scenes combined with Prefabs. Different games have vastly different ways of managing Scenes – no single solution works for all game structures. It makes a lot of sense to implement your own custom tooling to fit the organization of your project.

We hope this information can help you in your project or maybe inspire you to create your own Scene management tools.

Let us know in the comments if you have any questions. We would love to hear what methods you use to manage the Scenes in your game. And feel free to suggest other use cases you would like us to cover in future blog posts.

26 replies on “Achieve better Scene workflow with ScriptableObjects”

This article was interesting. Particularly the idea of dropping build time from 3 hours to 3 minutes. Can Unity rework some of it’s previous tutorials (rollerball, stealth, flyers) around this code model. It would be nice to see compare the architectures and see how using SO’s clean up the code, and what new ones they introduce.

Hi, it’s a bit off-topic, but could you please add likes/dislikes to the comment sections of blog posts? A lot of the readers have Unity accounts, which could be used to make our reactions.

That’s nice, sadly you cannot use ScriptableObjects as project-wide independant singleton (at least for WebGL), because it gets stripped! So you waste a day trying to understand why you access a null ref when not in Editor Play. To workaround that you absolutely need to reference the ScriptableObject instance in a GameObject somewhere.
For more information about ScriptableObjects you should check:

https://unity3d.com/how-to/architect-with-scriptable-objects
https://www.youtube.com/watch?v=raQ3iHhE_Kk
https://devansh.space/scriptable-objects-and-event-systems
https://medium.com/@adamramberg/unity-atoms-tiny-modular-pieces-utilizing-the-power-of-scriptable-objects-e8add1b95201

You can. You’d have to ensure that the SO is loaded from the start. One way to do it is using SetPreloadedAssets. A good example of that is Unity’s own Input System:
https://github.com/Unity-Technologies/InputSystem/blob/56e5c49289402898806927c1783d265f4989b9cc/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsBuildProvider.cs

If you already had another way to load the script (like resources, or an asset bundle, or referencing it from a scene) you can prevent it from being unloaded by setting the DontUnloadUnusedAsset Hideflag.

That being said, if you’re already able to put your singleton in a SO, I bet it wouldn’t be hard to get rid of the singleton pattern in the first place. Singletons can be very evil when you try to expand the mechanics of your game. SOs substitute them very gracefully, because you can just drag them to the inspector when you need to reference Singleton-Like object and at the same time you can have many of them available in any moment.

Glad to see multi scenes are getting some attention, as well as ScriptableObjects! (My favorite thing). This post is a good start, although it avoids most of the complications some others have mentioned. Regardless, I am a major proponent of ScriptableObject based scenes, as it is what I use in my game. In my case, I have multiple maps, which are split into chunks. In the end, I have around 500 chunks at the moment.

The key part of using this system is implementing editor tools to automate these sorts of things, which was mentioned.

If anyone’s interested, here’s some explanations on a scaled up example of using this strategy: https://www.ardenfall.com/blog/world-streaming-in-unity

That’s a great article, Joshua. I think one of the beauties of Unity is exactly that you can read about all of these different solutions, and then really make one that fits your needs. So thanks for sharing yours!

Great article, glad you’re advocating for the multi-scene approach. So many benefits!

Another HUGE benefit of breaking your game into lots of scenes is Asset Bundles. The workflow of sticking scenes related to a piece of content into an AssetBundle and just downloading it and Loading that scene is incredibly powerful in Unity, one of the most forward-scaling features imaginable, especially for small content developers. It’s just really easy to make ABs using Scenes. I’ve not tinkered with Addressibles yet, but I assume they’re analogous.

Nice start, but feels and looks like you’re solving a problem in isolation without considering all of the other complexities you might encounter in a game. How do you tackle lighting bakes? AI pathing? Persistent changes in scenes that get unloaded? How does it scale with really complex scenes?

I’d love to see something like this but built on top of a much more complex project — you can gloss of the technical details of how you solved some of those additional issues and maybe even cover some as additional blog posts, but the bare bones demo doesn’t give me confidence that this concept would survive coming into contact with a real game.

This, sadly. The only problem was the lack of details, so it’s more like a toy or an experiment than a solution. It’s a good idea, but knowing Unity some small piece of it may not like this technique without a lot of boilerplate code and corner cases.

That’s a fair consideration. As with many other comments on this post, the answer is that for the blog post we had to strike a balance between simple (and easy to explain) and realistic. We wanted to give the reader not a complete solution, but the tools to get started with one — which only they can make.
When you move more into the realistic realm, you inevitably also stumble in solutions that are very specific to the type of game, and we didn’t want to go there — for now.

Now the good news is: we will do something in that vein soon, so please keep an eye on the blog for announcements :)

While I appreciate these sort of posts for «intermediate level» users, I wasn’t entirely convinced by the quality of the pointers here. In fact, I fear it contains a few choices that’ll confuse people who really try to follow (or base work off of) these instructions.

First off, I commend the reliance on ScriptableObjects, which are a very useful tool that doesn’t always get due attention in tutorials, which are still dominated by the omnipresent MonoBehaviour. I think a fundamental note that could be added is that ScriptableObjects (like other assets) contain data on a project level, rather than on the level of a single scene.

Beyond that, however, the rest of the framework promptly forgets about that. The ScenesData ScriptableObject has a list of the previously created Level objects and… doesn’t use it at all. For loading scenes, it suddenly relies on some rigid naming convention even though the Level objects themselves store their associated scene name. It doesn’t take much imagination to picture a designer experimenting with switching items 3 and 5, only to find they’re still loading scene 3, but with the mismatched Level data of the former 5.

In fact, they’ll also be confused at always having to insert a dummy Level object at the beginning of the list, because the first item is somehow always skipped. It’ll take them some time to figure out that’s because the level index starts at 1, even though there’s also an element at index 0.

Also, they’ll probably be somewhat disappointed at how the text hints at an explanation of how to load a scene in different pieces, but the code doesn’t really deal with that. In fact, it only allows them to load Level0Part0, Level1Part1, Level2Part2 and so on, but no other permutations.

Later on, they may well be scratching their head why suddenly all their menus are linked wrong, even though those methods do actually use the scene name stored on the Menu object. Only later on will they discover their colleague added a new menu type at the top of the «Type» enum and now the entire list of Menu objects need to be reordered. Even later, they’ll remember the text warned against that instead of preventing it.

I apologize if I come across as a little overly critical here. It’s just that this same information can be found with higher quality and accessibility elsewhere, which is a weird thing to conclude about the engine’s official blog. I completely understand the desire to not want to overcomplicate things for a tutorial like this, but the code here introduces quite a few practices that are evidently fragile and that will confuse people who use it as a source of information. The linked video comparatively feels like a different level of quality.

Would you be so kind to elaborate on where exactly one would find the «other» source of information about scriptable objects used with scenes? I would be very much obliged.
Thanks!

Aw jeez, I might have been a little hyperbolic with that statement. It’s been quite a while since I’ve looked up tutorials on this particular topic myself. With regard to ScriptableObjects after a cursory internet search, these strike me as quite accessible and «realistic» applications, though really the potential is endless.

https://www.raywenderlich.com/2826197-scriptableobject-tutorial-getting-started
http://gyanendushekhar.com/2019/07/07/working-scriptable-objects-unity-tutorial/
https://www.youtube.com/watch?v=aPXvoWVabPY

With regard to breaking scenes into smaller bits: that tends to very specific to the game at hand. I came across this blogpost which has a succinct description of a world-streaming system. I don’t know if it uses ScriptableObjects anywwhere, but I could easily see them being used much in the same way as this blog post does: to wrap another object with extra data. In this case the «Cell ScriptableObject», beyond a reference to the scene/prefab itself, could hold information such as «what are the coordinates of this cell», «is there a settlement here», «is this hostile territory», «what music track should play»? This way, you could easily have a list of data for every cell in the world, at the memory cost of just a bunch of tiny ScriptableObjects.

https://www.ardenfall.com/blog/world-streaming-in-unity

Wow, thanks for actually scrutinizing the code, it gave me a good laugh. My bet is that it was written by an overworked engineer who had a million other things to do but was somehow roped into doing a blog post.

I wish there was a way to add a scriptable object to the pre-loaded assets list from the scriptable object’s inspector. (without me having to write it)

It is posible with this approach to bake Realtime global illumination information, for each scene separatelly, and then load it additivelly? We have a big open world, and bake the lighting, but we have to load all scenes and bake it togheter in a lighting scene, to then load that scene and keep it loaded all the execution time. There is a better way to achieve this?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *