Procedural Loading In Unity

Sean KumarAstra, UnityLeave a Comment

(Updated: Feb. 25, 2016) The blog post below is now outdated because multi-scene editing has been added in Unity 5.3. I am now using a single root scene which loads additively and unloads scenes according to what is needed, which makes things a lot easier to prototype and manage. There is no longer any need for Don’tDestroyOnLoad. This is the tip I’m using which can be found in the article http://docs.unity3d.com/Manual/MultiSceneEditing.html:

“It is recommended to avoid using DontDestroyOnLoad to persist manager GameObjects that you want to survive across scene loads. Instead, create a manager scene that has all your managers and use SceneManager.LoadScene(<path>, LoadSceneMode.Additive) and SceneManager.UnloadScene to manage your game progress.”

Simple and effective.

 

 

 

Old post:

(Updated: Oct. 2, 2015) By procedural, I mean loading managers and objects that are not already present at the start of a scene, based on certain factors and data.

This is not a tutorial on how to procedurally create an environment like in Minecraft. Rather, this is a sharing of one method of dynamically loading managers into a scene so that I can control loading order, load data from the file system, and make it easy add content to my game (or mod it) in the future. This may not be the right or best way to do it, but it’s the method I’ve settled on that fits my preferred workflow.

There are many reasons why I’ve decided to go down this route, rather than have many GameObjects and Managers already loaded with the scene at design time:

  1. I can’t rely on Unity’s Awake() and Start() methods to make sure certain scripts are loaded before others, as all present GameObjects’ Awake() methods are not called in a reliable order. Same with Start(). I initially tried to get around this by adding my scripts to Unity’s “Script Execution Order”, which works, but adding many scripts there may get a little unwieldy in the future.
  2. I want to make it easier to prototype various types of scenes. To do this, I’d have to add the same types of managers to every scene, and if they all reference each other in some way, even if just to subscribe to events, it will take a lot of time to set that up just to test something out. I tried singletons (if you don’t know what that is, there are plenty of tutorials and code examples of how to set one up… just search “unity singleton” in Google), as this is what many people have suggested. I’ve also read enough about why one shouldn’t use singletons. As a result, I came up with an alternate approach that I hope will work well in the future. You can find my solution further below.
  3. I’m going to be making the game as moddable as I can. For great moddable Unity games, see Kerbal Space Program or Cities: Skylines. I don’t have the technical know-how to accomplish what they did, but with Unity 5 and AssetBundles being available to all, I may not need their expertise. The approach I’m taking requires assets to be loaded dynamically at runtime, and so I need a solution to make that as easy as possible. I’ve been planning to make a blog post about creating a moddable Unity game, but I still have more testing to do before I share my discoveries.
  4. I wanted to stay away from dragging references into the Inspector as much as possible, even though that’s the Unity way. I’ve had some experience with projects losing those references due to various reasons, and it took time to hook all of them back up. I’d rather do this in code now, which requires just a tad more effort.

There are some drawbacks to procedural loading in Unity. The main one is limited use of the wonderful inspector in Unity that makes things very easy to set up. I still use it for editing prefabs and for entering data into ScriptableObjects, but I’ve found that I’m primarily using the inspector to monitor variables, as most of my game data is file driven now and not set in the inspector.

*Disclaimer* – If you’ve stumbled upon this blog and are new to programming and Unity, please keep in mind that this is about my personal journey of discovery in game development. There are many other ways to do game state management. Do a Google search for “unity game state management” to see quite a few. I now feel that one should follow a workflow that makes sense to them, as there are so many differing opinions about the “best” way of implementing something. What I’ve learned about programming is that I’ll consistently find better ways to implement a solution as I become more experienced. The problem becomes deciding how much time I want to spend refactoring my code to fit this newly discovered method vs. getting the game done. Decisions, decisions!

I decided that I only want one GameObject to persist throughout all scenes: a GameManager, which keeps track of and loads the various scenes and stores configuration data, paths, etc. that will constantly be accessed by various GameObjects in all the scenes.

The GameManager is the only GameObject that is consistently present at the start of every scene because it will dynamically load all the other managers depending on the name of the scene (or index). Here is what my basic hierarchy looks like in every scene:

Scene Hierarchy

 

 

 

 

Clean, right?

Actually, every scene will require just one other scene specific GameObject which subscribes to a GameManager event when the GameManager is finished doing its thing. This second GameObject will dynamically load content specific to that scene. I call it the SceneManager, or a scene-specific manager. Technically, if I set up the GameManager a particular way, even the SceneManager can be loaded in at runtime.

Back to the GameManager. As of now, the GameManager has 3 scripts on it (so far):

*edit* After revisiting this blog post to make updates, I now have something like 10+ scripts on the GameManager, which hold persistent data or perform various global functions.

GameManager Inspector

 

 

 

 

 

 

 

The Game Manager script is the central manager. The first thing it does is detect if there is already another GameManager in the scene (using FindObjectsOfType), and if so, destroys itself. If there isn’t another GameManager present, it calls DontDestroyOnLoad on itself to allow it to persist across all scenes. This way, in every scene, I can leave a GameManager present, allowing me to test just that scene without having to load the very first scene that loaded the first GameManager.

At build time, however, the very first GameManager which loads will be the one that persists, and when transitioning to a new scene, the GameManager present in the new scene detects the presence of the old one and immediately destroys itself, ensuring that the older one is the one and only.

The GameManager uses Awake() and Start() to start the dynamic loading process, whereas most of the other managers do not use those MonoBehaviour methods so that we can control load order. Instead, the other managers have an Initialize() method which is called by the GameManager, which performs the same functionality as what we’d have in Awake() and Start(). The nice thing is that in addition to controling initialization order, we can execute the method on other scripts even when they’re disabled if necessary.

After loading global data from the file system, the GameManager then detects what the name of the scene is (you can also do it by scene index) and decides which types of managers to load based on the scene type.

The loading of all the other managers, the UI, and camera will be done in a coroutine. Why a coroutine? Well, dynamically loading a scene makes it useless to use Application.LoadLevelAsync for updating a loading progress bar, since only the GameManager itself will be loaded. Using a coroutine allows me to load specific things in each Update frame (using yield return null), allowing me to set loading text and update a progress bar on each frame (the function of the Status script is to display loading progress in the UI).

Remember, every scene will have a second scene-specific GameObject other than the GameManager which will load content after the preloading has finished. It does this by subscribing to an event sent by the GameManager after preloading finishes. Once the event is handled, this scene-specific script will do whatever is necessary to dynamically load all the models, textures, etc. that is required for this particular scene, possibly also in a coroutine so we can continue updating the loading bar. Because the managers are already loaded at this point, all dynamically loaded assets can reference those managers using FindWithTag or some other method (if using singletons, through Instance).

Actually, I’ve chosen not to go with Singletons for the managers due to reading several posts about hard-to-find memory leaks as a result of singletons and static instances. I’ve also seen solutions to those problems, but I’d rather not risk it with my current knowledge level. One interesting alternative is using one script which holds all manager references, providing static methods to access those variables, removing the need for singletons.

So overall this is what I’m going with for now. It’s working well for me so far, and although there may be many more efficient ways of procedurally loading managers and objects, I need to get on with making the game. When I find a much better way of doing this, I’ll consider changing it then (as I’ve already done quite a few times already!).

Thanks for reading.

Leave a Reply