Unity Tip: Don’t use your first scene for global Script initialization.
I’ve noticed that a lot of projects online seem to use the first scene as a means of initializing global systems.
Why this is bad
Doing this means you are making yourself more dependent on your initialization scene.
I’ve seen a person streaming online, loading in the first scene countless times. Going from
Intro (Load a lot of stuff) -> Main Menu -> Level Select -> Your Level.
If you just want to test your scene, then this can become very tedious over time.
Making a game takes a lot of time, it is important to invest in making your iteration process quick and easy.
The solution
I’m happy to tell you there is a easy solution for this problem, using the [RuntimeInitializeOnLoadMethod] attribute.
In this case I’m going to demonstrate a way to have a prefab in your project that always loads first.
Create a prefab called ‘Main’ within your Resources folder, and attach this script to it.
Attaching the script is not mandatory, but in case you want to extend it in any way you can choose to do so.
Being able to set inspector fields can be beneficial, which is why the sample below still inherits from MonoBehaviour.
public class Main : MonoBehaviour | |
{ | |
// Runs before a scene gets loaded | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] | |
public static void LoadMain() | |
{ | |
GameObject main = GameObject.Instantiate(Resources.Load("Main")) as GameObject; | |
GameObject.DontDestroyOnLoad(main); | |
} | |
// You can choose to add any "Service" component to the Main prefab. | |
// Examples are: Input, Saving, Sound, Config, Asset Bundles, Advertisements | |
} |
This script will ensure the prefab gets loaded before anything else.
This means you can attach any Service/GameObject to the prefab, and it will be guaranteed to load first.
Eliminating the possibility of any racing conditions. And the benefit of the services always loading, no matter what scene gets loaded.
A use example. For instance I want to use Graphy, A free performance monitoring tool.
Now I can just add graphy to the Main prefab, and it works in all scenes, even new ones.
One thing to keep in mind: Don’t reference any big assets within the Main prefab. As all references are also stored within the Resources folder. If you want to have some kind of asset loading system, I would recommend looking into the Adressables package or Asset Bundles.
What do I use this solution for?
I generally like to create service classes that are available before any scene is loaded.
For instance, to make sure that:
Config -> Setup global settings for game, store global configurations (Like volume)
Saving -> Initializing the save system
Audio -> Initializing a audio system. Being the fact that it is a prefab, it easily allows me to reference a Mixer
Input -> Initializing, configuring Rewired a plugin that makes it easy to get input on any platform.
I use it to add input support for mobile games. Including Masked.
Hi, I’ve trying your code, but I have a problem, the GameObject main always is null. Do you know what happends? Thanks
Hello Ruben, are you sure that you have a prefab called Main in your resources folder?
All Resources.Load(“Main”) does it tries to find a Prefab or Asset called Main within the Assets/Resources folder.
The pattern I then use is I apply components to that main prefab to ensure those are
loaded in before anything else.
Hope this helps!
Thank you for sharing!
This helps me refactoring some dirty initialization logic 🙂
Dear Alex, I’m also trying to instantiate the gameobject main and it is equal to null. And I checked multiple times to see if the gameobject is in my resources folder? Also Ruben did you possibly find a solution?
Hello Maurice, this is odd. May it be that you have multiple folders called “Resources” in your project?
I believe some older versions of Unity do not support multiple folders, meaning that the folder has to be at the root of the project. Also, you must be 100% sure that the name of the prefab in the code matches the prefab name.
I’ve made a UnityPackage containing example files that work on my end. I’ve tested it with Unity 2019.1.7f1
https://drive.google.com/open?id=1bm3IyUsCwRfo-8XzA4hREVSVUUulXqRD
This is not bad at all. Especially when you have a running/async initialization process and have to wait for it before continuing to the scene. E.g. you have an online game and need to setup the connection first.
The solution – just load the ‘initialization’ scene in Awake of your normal scenes, save current opened scene name and reload it after initialization is done.
Works like a charm!
It really depends on the setup of the project.
The attribute allows you to automate this process more, no need to rely on a component that calls something on Awake.
You can execute code based on: the path of the active scene. Root objects that are available in the scene… etc.
Removing any potential manual labor.
For instance, I have a multiplayer arena scene, and I want to test it. I have to add component x to the scene?
Assuming I also have to tag this component as EditorOnly. While you can also just apply logic for any scene that has “Multiplayer” or “Gameplay”
in the path. Or the actual name itself.
I’m so glad I found this, it’s made things so much easier. Thank you!
Hello I like your idea but I use same technique with scriptableobjects.
1. The first trick is to use scriptableobject as singleton. So this means I don’t even need to load anything. If scriptableobject gets loaded (via RuntimeInitializeOnLoadMethod) so it already its in game. You can have there example prefabs full of different types for example enemies to spawn them. Only bad thing is here if I do changes it will stay persist. But sometimes it is ok. If I have a boolean debug and its turned on so I can see more prints in the console.
2. Method is ScriptablePrefabSpawner. This helps me example to spawn on on load a prefab. Via this method I don’t even need to code something in the scriptableobject. Just drag and drop prefab and everything else will work with monobehaviours. But also very nice to spawn something like FpsCanvas everytime I go into playmode (or start the standalone game).
I prefer to do 2. method which is very nice. No need for Resource.Load. But the ScriptableObject itself need to be in the resources folder.
“Intro (Load a lot of stuff) -> Main Menu -> Level Select -> Your Level.”
– Exactly, this was my problem as well in 2015-2016. I was looking for solution. This both method helped me to not spawn into the scene but if I was forced like the a canvas so I spawned them.
I am using a “Root Scene” as a global script initializer of my own making. Coincidentally I was not influenced by tutorials – because I have not seen any regarding this implementation. However, I am wondering if this approach would ever fit my use case.
Suppose I have multiple independent but connected levels.
Suppose I can move from level A to B by going through a passage, then, from B, I can go back to A or go to C, and so on.
Each of these levels needs access to a few objects (like the ones you exemplified). In particular, they need access to an Audio Manager, which ensures that the music keeps playing even when moving across scenes. Switching soundtracks can only happen when the “main loop” of the previous level soundtrack terminates. The transition must be seamless. For this, endeavor I used additive scenes to manage all my levels and created a SceneLoader flow.
Do you think this would work? In other words, I guess my question is: the approach you documented ensures that a given set of objects, which you called services, is always available on any scene just before it loads, but does it “reset” those services’ state, thus disrupting my “seamless music transition”?
As long as the objects are marked DontDestroyOnLoad during the InitializeOnLoad method, then everything persists.
So if you have a AudioSource attached to Main, it will keep playing. Despite of any scene changes you do at the beginning.
Hey I wanted to ask.
Can this store an array of for example 20 cars that player can choose from? What I mean here is that I want to use this to store all the cars in array, then when a scene loads, based on that array, I spawn the car that player picked in the menus.
Do you think it’s doable?
Apologies for my very late response.
This method is mainly useful for anything related to initialization when the application starts.
In your case, I would try to create a script that holds a list/array of possible cars in your game.
Which you then fetch either through a ID that points to resources, or an ID that fetches a addressable.
You could then instantiate this script using [InitializeOnLoad] by having it inside a prefab.
So its always available despite the scene you start in.