Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Unity script order execution

Optimizing Script Execution Order in Unity

One of the most common pitfalls in Unity game development is the dreaded “NullReferenceException”.
An essential factor in avoiding this error is maintaining a well-organized script execution order.

If you’re looking for a comprehensive explanation of Unity’s event execution order, the official documentation provides detailed insights.

Table of contents

Modifying Script Execution Order

Changing the execution order of your scripts primarily happens within the Unity editor.

  1. Navigate to “Edit” -> “Project Settings” -> “Script Execution Order.”
  2. Then, you can click the “+” button to add your script
  3. Define how many milliseconds your script should wait before execution (On the milliseconds field negative values are allow).

Unity script order executionUnity editor – Script Execution Order window

Modifying Execution Order in Code

It is also possible to adjust the execution order directly in your code using the “DefaultExecutionOrder” attribute.

Notably, this attribute is not officially documented. As one forum post suggested, the reason behind this could be that the “DefaultExecutionOrder” attribute’s effects do not appear in the Execution Order window, potentially leading to confusion.

using UnityEngine;

[DefaultExecutionOrder(-50)]
public class Script : MonoBehaviour
{

}

Automating Execution Order

Changing the execution order can be repetitive and cumbersome, especially if you have a substantial number of scripts to manage.
However, to address this issue, you can define a folder where any newly created scripts automatically have a predetermined execution order.

Achieving this involves creating a file “CSAssetPostprocessor” within an “Editor” folder, which inherits from “AssetPostprocessor“.

Be careful, on Unity 2021.2+ “OnPostprocessAllAssets” has an additional bool parameter: “didDomainReload“.

Follow this link to learn more about domain reloading.

using System.IO;
using UnityEditor;

public class CSAssetPostprocessor : AssetPostprocessor
{
	// Be careful, on Unity 2021.2+ has an additional parameter: bool didDomainReload
    private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        foreach (string assetPath in importedAssets)
        {
            HandleImport(assetPath);
        }
    }

    private static void HandleImport(string assetPath)
    {
        // only cs files
        if (Path.GetExtension(assetPath) != ".cs")
        {
            return;
        }

        // only file in directory "BeforeDefaultTime"
        if (Directory.GetParent(assetPath).Name != "BeforeDefaultTime")
        {
            return;
        }

        var monoImporter = AssetImporter.GetAtPath(assetPath) as MonoImporter;

        // avoid infinity loop
        if (MonoImporter.GetExecutionOrder(monoImporter.GetScript()) == -100)
        {
            return;
        }

        // SetExecutionOrder -100 ms
        MonoImporter.SetExecutionOrder(monoImporter.GetScript(), -100);
    }
}

Considerations and Alternatives

While adjusting script execution order works well for certain scenarios, it may not be the best approach when you need to sequence scripts without prior knowledge of their execution duration (e.g., for handling HTTP requests, sub-scene loading …).
For such cases, it is often more straightforward to manage script execution order directly in your code.

Consider a game that needs to load data, then load a level, and finally load enemies. One approach is to have a primary script, such as “GameBootstrap”, responsible for the overall game state. It is essential to implement it as a singleton for consistency.

To streamline the process, you can create an abstract class, “AbstractLoader”, serving as a reference point for each state.
Don’t worry about the error on “GameBoostrap”, we’ll create it right after.

using UnityEngine;

public abstract class AbstractLoader : MonoBehaviour
{
    // System.Serializable -> we are going to use it in inspector
    [System.Serializable]
    public enum StateLoad
    {
        NONE,
        DATA,
        LEVEL,
        ENEMIES,
        FULLY_LOAD
    }

    public StateLoad stateLoad;

    public StateLoad stateRequired;

    public bool IsLoad { get; private set; } = false;

    abstract public void Load();

    // Must be called when load is done !!!!!!
    protected void LoadDone()
    {
        IsLoad = true;
        GameBoostrap.Instance.OnStateLoad?.Invoke(stateLoad);
    }
}

To clarify, we use the enum “StateLoad” to identify a loader.
The “stateLoad” field is the identifier, while the “stateRequired” field allows for the inclusion of a dependencie.

Next, create a gameObject with the “GameBootstrap” component in your scene:

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using static AbstractLoader;

public class GameBoostrap : MonoBehaviour
{
    public static GameBoostrap Instance { get; private set; }

    public List abstractLoaders = new List();

    public UnityAction OnStateLoad { get; set; }

    private void Awake()
    {
        if (Instance != null)
        {
            throw new UnityException("One instance only");
        }

        Instance = this;
        // you can add DontDestroyOnLoad, if you want it to be persistent accross all scenes
    }

    private void Start()
    {
        ApplyLoaders();
    }

    public void ApplyLoaders()
    {
        // prepare load
        OnStateLoad += OnStateLoadAction;

        // load only, loader without stateRequired
        foreach (var loader in abstractLoaders)
        {
            if (loader.stateRequired == StateLoad.NONE)
            {
                loader.Load();
                continue;
            }
        }
    }

    // When all is load it's true
    private bool _isFullyLoad => abstractLoaders.All(x => x.IsLoad);

    // cache of _isFullyLoad, for external call
    public bool IsFullyLoad { get; private set; } = false;

    private void OnStateLoadAction(StateLoad stateLoad)
    {
        // all is load, we are going to close the event
        if (_isFullyLoad)
        {
            FullyLoadAction();
            return;
        }

        // load when stateRequired is done
        foreach (var loader in abstractLoaders)
        {
            if (!loader.IsLoad && loader.stateRequired == stateLoad)
            {
                loader.Load();
            }
        }
    }

    private void FullyLoadAction()
    {
        OnStateLoad -= OnStateLoadAction;
        IsFullyLoad = true;
        OnStateLoad?.Invoke(StateLoad.FULLY_LOAD);
    }
}

To maintain consistency, it’s advisable to consistently declare your instances within the “Awake” method.
This ensures that an instance is established before being accessed from other scripts, and it should always be invoked during or after the “Start” method lifecycle.

In this code example, all “NONE” state loaders are initially loaded.
Subsequently, once a loader finishes, loaders dependent on it are triggered until all loading is complete.

Now, to test the process, create a script that simulates loading for each of the three states, such as “ExampleLoader”.

using System.Collections;
using UnityEngine;

public class ExempleLoader : AbstractLoader
{
    public override void Load()
    {
        Debug.Log(stateLoad + " is starting");
        StartCoroutine(SimulateDelay());
    }

    // simulate http request ...
    private IEnumerator SimulateDelay()
    {
        yield return new WaitForSeconds(Random.Range(1f, 3f));
        Debug.Log(stateLoad + " is done !");
        LoadDone();
    }
}

Create three GameObjects with the “ExampleLoader” component and associate them with “GameBootstrap”.

Unity order execution Custom

Result in console:

Unity order execution console

Execute actions outside workflow

Finally, suppose you need to perform specific actions after a state is loaded without being in the workflow.

In that case, you can add the following lines of code to “GameBootstrap”.

    // check if a loader is load
    public bool IsStateLoad(StateLoad stateLoad)
    {
        // there is not, loader with state FULLY_LOAD, IsFullyLoad does this job.
        if (stateLoad == StateLoad.FULLY_LOAD)
        {
            return IsFullyLoad;
        }

        return abstractLoaders.Single(x => x.stateLoad == stateLoad).IsLoad;
    }

    public void ExecuteActionAfterStateLoad(StateLoad stateLoad, UnityAction unityAction)
    {
        // State is load, we invoke right away
        if (IsStateLoad(stateLoad))
        {
            unityAction.Invoke();
            return;
        }

        // State is not load, we subscribe to the event (I like use internal void, to unsubscribe the event)
        void WaitStateLoad(StateLoad state)
        {
            if (state == stateLoad)
            {
                // must be unsubscribe
                OnStateLoad -= WaitStateLoad;
                unityAction.Invoke();
            }
        }

        OnStateLoad += WaitStateLoad;
    }

Here’s an example of how to use it:

using UnityEngine;
using static AbstractLoader;

public class TestWaitDataLoad : MonoBehaviour
{
    void Start()
    {
        GameBoostrap.Instance.ExecuteActionAfterStateLoad(StateLoad.DATA, () =>
        {
            Debug.Log("Execute after data");
        });
    }
}

Unity execution order console out workflow

This last point is a little off-topic, but mainly serves to illustrate the fact that you shouldn’t use “order execution” in all cases.

Conclusion

To conclude, we’ve seen how to modify the execution order on unity, starting with the editor, moving on to modification by postProcessor and ending by creating our own Manager.

Don’t forget to take the time to analyze your needs so you can use the best solution.

I hope you’ve found this article useful, and enjoy your coding!

Learn More

Have you just started your Unity adventure?
Check this article to start in the best possible way: The Best Advice I Wish I Had When I Started Game Development

Check our other articles about Unity:

Leave a Reply

Your email address will not be published. Required fields are marked *