Over the past few months as more and more detail and complexity has been added to the maps (especially the larger ones), I began to notice a gradual increase in the time it took to switch maps, to the point where it was now taking as much as 30 seconds to load one of the main maps. Something needed to be done!
The first thing I did was perform some tests to see where the time was being taken, so I could work out the best strategy to tackle the problem. What I noticed was that after a scene change was triggered Unity effectively locked up for the duration of the loading of the next map, so I thought maybe using LoadSceneAsync might somehow come to my rescue.
It soon became apparent however, that it wasn’t Unity’s scene loader that was the problem, because Load Scene and LoadSceneAsync were both completing in 1-2 seconds when I timed them, and then Unity was sitting there doing nothing (from the player’s viewpoint) for the rest of the 30 seconds.
In fact it wasn’t doing nothing, it was busy setting up all the stuff that forms the scene as designed in the editor. This ‘glitching’ during scene loading seems quite a common problem judging by the Google search results, and the usual advice is use LoadSceneAsync and just switch the new scene in one it’s loaded. The problem with this approach is, that when LoadSceneAsync has finished, the baulk of the work is yet to be be done, i.e. loading and instantiation all the prefabs, meshes and other resources that go to make up the scene, and it does all this work on the main thread, and stalls everything else until it’s finished. Which can take a loooong time for a complex scene.
Following this discovery, I worked out, by selectively removing stuff from the scenes, that the long setup time was split roughly 50/50 between all the scene geometry and all the mobs, so I decided to tackle each of them separately.
I was already aware of an asset called MegaScatter, which promised to deal with exactly this type of problem for objects placed in scenes, so I purchased a copy of it and got to work adapting one of the maps to make use of it.
It has two methods of dealing with scene objects, it can either collect up a group of objects and combine them into a single mesh, or it can generate a list of the objects and spawn them at runtime. (Either way the originals should be removed from the scene before saving.)
Thinking that combined meshes were going to be best for performance, I went with that as the first option. Two problems here though…First of all, the mesh data was stored in the scene file, which meant that the scene file quickly exceeded the 100MB max file size for Github, so to get around this I saved the meshes made by MegaScatter to assets instead. But the second and killer problem, was that loading the combined meshes actually increased the time it took for the scene to finish initialising!
Okay, I thought, I’ll use the second option and have MegaScatter instantiate all the bits and pieces after the scene has loaded, it has an option to restrict the amount of items instantiated each frame, so this should prevent frame rates tanking while it’s doing so.
However, no matter what I tried, Unity just sat there while all the objects were being instantiated (with nothing appearing each frame), until everything all appeared at once after a number of seconds, during which time framerate was zero. Not what I was expecting from the way it was described in the manual! Also, there doesn’t appear to be any way to instantly instantiate selected bits based on the location of the player, so there is the risk that after the map loads the player could see some of the later instantiated items pop into view.
The practical upshot being that MegaScatter didn’t help. But, I knew that spreading the creation of the scene objects over a short period of time had to be the way to go, so I set about creating my own scattering system. With a bit of experimentation I finally worked out a method that quickly and easily allows me to create and maintain scene objects in the editor, which aren’t saved with the scene, but are instantiated at runtime, without any of the problems outlined above.
In my next post I’ll describe how this works. But for now here’s some screenshots of some work I’ve been doing on one of the maps. (Because of variations in monitors some forum images as posted may be too dark/bright, but the game has settings to compensate for this).