unity

Large Scenes in Unity 5.3

Overview

I have been playing around in Unity for a couple of weeks now and I am attempting to do a game with lots of entities that are prearranged in a very large map.  It is a minimal exploration art game.  I was looking for an approach to manage a substantial amount of entities in a large scene.  This is both a performance issue and an operational one.  I assume it would be hard to manage a large scene with thousands of entities.  Not only would editor load times stifle the creative process, but traversing through a large level hard to manage.  I would also assume that multiple designers working the single scene would be challenging.  After doing some digging I found some nice tutorials on creating open world games (GTA, Skyrim, etc),  but they do not address the large scene problem.  After some digging, I noticed some methods of the SceneManager class of the Unity framework.  The specific function in question are the additive and scene merging functionality. There did not seem to be much documentation or examples of the use of the SceneManager's additive load functionality.  

My game would have hundreds of these unique sections each containing dozens of entities.   The solution that is presented below allows for a clean way to organize a large world with many entities.  It would allow for many developers to author in this large world and minimize performance issues of having many objects loaded simultaneously.

This is a simple tutorial, and the first article I have written in a long time. Hopefully some of you will find it useful.  As well, I would love any feedback on my approach.

 

 

Approach

Here is the approach:  There are many scenes, each containing a small and specific section of the of large world.  A master scene contains a set of triggers that load/unload each section (I call them subscenes) based on the player location.   Each section are oriented in its own local space at (0,0).  When loaded, we transform all the game objects by a transform that maps the locally oriented objects of the section into the coordinate space of the large world. 

We will be using the additive loading functionality of the SceneManager.  The following lines of code will load and unload the level content.


      SceneManager.LoadScene( aSceneName, LoadSceneMode.Additive );

      SceneManager.UnloadScene( aSceneName );

 

Master Scene - A master scene contains a set of triggers that load/unload each section based on the player location.  It also contains the Player,  Game Controllers and other subsystems of your game.  Below is a depiction of what our Master Scene might looks like.  It only has four nodes right now but the idea is that we would have hundreds.  Also the example imply some sort of fantasy theme.  This example was picked to help articulate the approach.  Each green circle is a unique SubsceneTrigger game object that corresponds to a specific subscene.  The subscene is loaded when the player enters the collision region and unloaded when the player exists the region.   Each SubsceneTrigger entity is responsible for loading/unload its associated scene. 

 

SubsceneManager - There is not much here.  We justneed a place to keep track of the Subscene-To-World-Space transform.  This position is set to the transform of current SubsceneTrigger that the player is in proximity to.   We assume that our Subscenes will be axis aligned with the world transform.   Eventually we may add more functionality to the SubsceneManager. SubsceneManager could be made Signleton or added to you're GameController.  See the improvements section for additional details on what we may use the SubsceneManager for.

 

SubsceneTrigger - A SubsceneTrigger will be responsible for updating the SubsceneManagers Subscene-To-World-Space transform.  When the player enters a SubsceneTrigger, the Subscene-To-World-Space transform is updated.  We only use position in our example because we assume our Subscenes transformation contains no scalar or rotational aspects.  The SubsceneTrigger will also also be responsible for the loading/unloading of each scene.  Here is how the SubsceneTrigger game object looks like in the Inspector.  We require a Collider trigger actuate the loading/unloading of the subscene.

 

The code is quite simple.  We listen for the collision trigger events from the associated Collider2D component.  Feel free to use the collision2D shapes of your choice.   Note, there is a bug that will crash Unity if the UnloadScene is called from OnTriggerExit.  We differ it using a coroutine.  We could also move it to the Update as well.  Here is the thread that discusses the specific Unity bug.

 

 

Subscene - Now that we can load and unload subscenes, lets take a look at the subscene.  To bypass the necessity of transforming each of the entities of the subscene, we can simply add all entities of the subscene to a root game object.  This root node will reference the SceneManager and apply the Subscene-To-World transform.  The script that accomplishes this is theSubSceneRoot script. The code is quite simple.  We just need to set offset the root gameobject's (SubsceneRoot), by the Subscene-To-World transform. 

Note that all game objects the subscene are children of the SubsceneRoot.  We author our sub scenes around the origin (0,0).  Each subscene must be structured this way.  Alternatively, each entity could also each contain the SubsceneRoot script but that would be much more of a performance burden.

 

Conclusion

That's about it.  It is a pretty simple technique that can make large worlds viable from a performance perspective and much easier to operationalize. This technique would work well for large open world games.  I have added some notes about improving the system below. 

 

 

 

Improving

Visualizing the World - One of the challenges is that it is hard to view the entire world in the Master Scene. We can solve this by adding some debug drawing code.  The Gizmos for the CollisonRegions also helps to visualize this.

We can also add a editor button from the selected SubsceneTrigger to goto the associated scene.

There are some other methods that may be able to achieve this.  The documentation presents MergeScene.  This method sounds like it could work but I have not had any luck with it.  Hopefully, I can play around with it and get it to work.  The LoadSceneMode.Additive presented in (1) works perfectly.

Performance - If this is an issue you can grid align the World into uniformly sized cells.  This would avoid having to use the Collider component and reduce the number of Game Objects.  The subscenes in my game are non-uniformly spaced out so I am using the CirlceCollider2D. 

3D Implementation -  The same approach can be achieved in 3D.  Just use the properly 3D Collider Components and call backs (OnTrigger3D, etc).