Scene Management in Unity | Code Review

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
We're back with another code review video, in  this series I show you some actual code I write for actual video game productions, and today we're  looking at scene management, I made a cool system to help me manage scenes in Unity, and I think  it's pretty neat we're going to check it out! So Unity relies heavily on the concept of scenes,  whether you use them at stages or regions in the game you will switch between scenes using the  Unity SceneManager. You can also load multiple scenes at once using Additive mode, this way you  can have the UI in a separate scene for instance, because it's common to all scenes. Our game has a pretty standard setup: we have multiple scenes for multiple regions, the home of the  player, the village, the docks, other areas of the world, and we have a Common scene with all  the UI, important systems, audio stuff, whatever is common to all scenes. However since we have  gorgeous 2D Graphics as you can see, every scene is hhheavy, and when switching scenes loading  times can be quite long. Which as the player is annoying not gonna lie, so we'll want to  try and avoid that as much as possible. The way we do this is we load every scene in Additive  mode, this way once the scene is loaded and we exit the area, we simply disable the contents of  the scene, and when we re-enter it later we enable it again. As a result the initialization logic  of a scene happens in the OnEnabled method as the behavior must be the same whether you're  enabling a scene or loading it fresh. To keep track of scenes our own SceneManager takes  care of loading loing the scenes, calling the Unity manager under the hood, and keeps track  of what scenes have been loaded, this way when we want to switch scenes the manager can choose  to either load a scene or simply enable it if it was already loaded. Scenes are referenced using  a handy-dandy scriptable object called SceneAsset. A scene asset represents a scene and  that's how we reference it when we want to, say, teleport the player to a scene. So what's in the  SceneAsset? Well first of all a reference to the actual Unity scene, I use this very useful script  (link in the description) that creates a scene reference field and tells you whether it's in  the build or not. The one I use isn't maintained anymore but it still works, and there are plenty  others on GitHub as well. Along with that we have a weight amount, we'll get to that in a bit and  a list of neighbor scenes, for instance from the Docks you can go to the Village from the Village  you can also go to the Docks or to the Forest or the Player's home etc etc and we'll get to see how  that's useful in a sec as well. Basically think of the scene asset as the metadata of the scene,  it's useful to handle scenes and metadata without actually having to load them to know what's inside.  In the future more data will be added such as for instance the sound banks used by the scene. So in  order to enable or disable an entire scene at once every scene has a single root object, this object  is always active but when enabling or disabling a scene who will toggle its children. And this  object has a SceneRoot component which helps with various things, such as loading the common  scene if it's not loaded, this way I can start the game in the editor from any scene without  worrying about loading the common scene. So how does loading actually happen? Well when we want  to switch to a new scene we will load the target scene first obviously, the loading function itself  is in the scene asset, which first checks if the scene is pre-loading (we'll get to that later) and  then checks if the scene is already loaded by checking if it is aware of any scene root object  being present for this scene. If it isn't loaded, we'll call the Unity LoadScene function and wait  for loading to complete, then return the scene root. Back to our scene manager we just have  to disable the scene we just exited and enable the new scene, whether it was just loaded or was  already in cache. Now as I said we load scenes and disable them when we don't need them this way they  are already loaded next time we want to visit them but obviously if we never unload scenes, at some  point we will run out of memory, especially on lower end hardware. That's where the Budget System  kicks in. I mentioned every scene has a Weight field, this is actually a rough estimate of the  weight of the scene in RAM in megabytes, in the scene manager we set a total budget for scenes and  we will keep loading scenes additively as long as we are within budget once we reach the limit if we  want to load a new scene we will first unload as many scenes as needed to free up enough space for  the new scene to load. The simple way to know which scene to unload is to maintain a history of  what scenes have been used every time we enable a scene, we move it to the front of the queue and  when loading we remove the oldest scenes first starting from the back of the queue. This heuristic  is not optimal imagine a case where the player leaves home, travels through multiple regions, then  wants to teleport back home. By then the home scene might have been unloaded. What would be better  would be to define a sort of priority or rating system based on frequency of use to make sure we  keep the most frequently visited scenes in cache. But anyway that's how the budget system works and  it has additional benefits for instance the total budget limit can be set to different values based  on the platform as there isn't the same amount of RAM on say the Nintendo Switch than on more recent  consoles. We could even imagine setting the budget dynamically on PC based on the player's hardware.  An other thing we can do is to preload scenes as I said every scene asset has a list of neighboring  scenes so we could preload them as long as we stay within budget this way by the time we actually  get to the door to the neighbor scene it might already be loaded or almost loaded. Preloading is  very cool or rather it would be but this feature is disabled right now. Let's go on a little tangent  to explain why. So when loading a scene there are a few spikes that freeze the game like maybe three  or four frames that take around 50 milliseconds and it's not a problem while switching scenes  at worst the loading spinning icon thing just stutters but preloading happens during gameplay and that's unacceptable investigating that led to discovering the CPU is waiting on the  GPU to render the frame which is weird because there's nothing to render yet as the new scene is  disabled so I checked the render thread and it's actually uploading textures to the GPU using Gfx.UploadTextureData. since we have huge background textures and they haven't been optimized at all  that's no surprise. There is a feature in Unity to avoid this, the solution is to set the async asset  upload settings in the quality settings. You can set the size of the upload buffer (but really  you should set it to the max texture size you can because if Unity encounters a texture larger  than the config it will reallocate the buffer). You can also set async upload time slice which is the  number of milliseconds we allow the uploading of textures to take per frame. So that's cool right?  Wrong. Setting it to a low value (the default is 2ms) did not change anything. My theory is  this setting is here to allow multiple uploads per frame, like after a first upload we'll check if we  still have time and start a second upload, but if a single texture takes 50ms to load, it  will take 50ms and it's not going to be split over multiple frames. So we're kind of  stuck actually, if we can't split the upload of a single texture and we have very large textures  they will each create a spikes. And that sucks, the only thing we can do is optimize the textures. Right  now since we're in development and I don't really care about stutters or loading times, all images  are uncompressed and don't have a Max Size, in the future I will need to bring them down in size  while compromising the least on quality, and for this we have a bunch of ways to optimize a texture  but that is planned for another episode of Code Review All That Remains is a way to measure  the weight of a scene, the way I do this is by building a project with just an empty scene and  the scene we want to measure, in the empty scene I take a snapshot of memory usage using the memory  profiler, then switch to the Target scene and take a second snapshot. The difference between the two  is the weight of the scene. In the future I hope I can automate this process by having a CI Pipeline that loops on every scene, builds the measuring build, launches it, takes the snapshots,  reads the difference and sets the value of the scene asset weight automatically, but that is a very  low priority feature considering the other stuff that needs to be done for the game. Now of  course there is more to RAM than just loading scenes, and the total budget mustn't be equal to  the total amount of RAM available, to account for dynamic objects that will be created or instanced. So during loading time the game fades to black, right, and it fades back in when the scene  is loaded, but the scene being loaded on the Unity side doesn't necessarily mean it's initialized  on our side, there is some additional stuff we want to do like run time navmesh generation or  making sure other resources are ready before proceeding. So in the awake function objects  in the scene can register themselves in the SceneRoot as being objects that take a while  to initialize. The scene root keeps a list of those initialization observers and during a  scene switch we will wait for all of them to be ready before removing the loading screen.  That's it I think for scene management let me know what you think in the comments and  how you handle scenes and loading times. In the future I will make a similar system for  addressable assets with a budget of its own as a way to manually keep track of assets and  to load a bundle exactly once with a reference counter to know when it's safe to unload it  You can grab the source code for the scene management tool on the patreon and use  it in your project there's also another neat little tool for navigating scenes as a patreon  exclusive. See you soon and have a good one!
Info
Channel: Useless Game Dev
Views: 5,682
Rating: undefined out of 5
Keywords: devlog, gamedev, indiedev, scenes, unity
Id: wj-cHmFYdQE
Channel Id: undefined
Length: 10min 16sec (616 seconds)
Published: Mon May 20 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.