Awake/Start/Update call order undefined between two monobehaviors.

RESOLVED

Hi,

I have a prefab that I instantiate with two Monobehaviours (A & B) attached to it. When I instantiate the prefab, the order of the Awake/Start/Update functions between A & B is undefined. I get different orders, e,g sometimes:

Awake-A
Start-A
Update-A
Awake-B
Start-B
Update-B

or, sometimes:

Awake-A
Awake-B
Start-A
Update-A
Start-B
Upate-B

Within one Monobehaviour, the order or Awake->Start->Update is always clear.

Unity version: 2021.1.23f1

This seems to be the same issue as in:

but this post is from 2009 with no resolution, with the only Unity staff comment being “the behaviour you are seeing […] is very strange.”

Are there any news, if this is this intended/accepted or a bug?

Something is weird in your project or you’re doing something weird. You probably should submit a bug report with a minimal reproduction project.
I built a quick test-rig to check what you’re saying and I get completely normal results:

TestA.cs

using UnityEngine;
public class TestA : MonoBehaviour
{
    private int _c = 0;
    private void Awake() => Debug.Log("Awake A");
    private void Start() => Debug.Log("Start A");
    private void Update()
    {
        if (_c != 0) return;
        Debug.Log("Update A");
        _c++;
    }
}

TestB.cs

using UnityEngine;
public class TestB : MonoBehaviour
{
    private int _c;
    private void Awake() => Debug.Log("Awake B");
    private void Start() => Debug.Log("Start B");
    private void Update()
    {
        if (_c != 0) return;
        Debug.Log("Update B");
        _c++;
    }
}

Test.cs

using UnityEngine;
public class Test : MonoBehaviour
{
    [SerializeField] private GameObject prefab;
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A)) Instantiate(prefab, Vector3.zero, Quaternion.identity);
        if (Input.GetKeyDown(KeyCode.S))
        {
            var go = new GameObject("bla")
            {
                    transform = { position = Vector3.zero }
            };
            go.AddComponent<TestA>();
            go.AddComponent<TestB>();
        }
    }
}

Prefab
7555558--934270--screenshot1.png

Results (no matter which one I execute, the results are the same):
7555558--934273--screenshot.png

Thanks for your reply and test.

I created a minimal example prefab to test. When I just instantiate very straight forward:

            if (Input.GetMouseButtonDown(0))
            {
                GameObject go = Instantiate(Prefab, Vector3.zero, Quaternion.identity);
                go.name = $"TestInstance - {index++}";
            }

then it works as expected.

However, when I instantiate it via my internal system, it gives the strange behaviour as described above. I’m still using a simple Instantiate:

    private void BuildTower(TowerSocket towerSocket, BuildingBase selectedBuildingPrefab)
    {
        Debug.Assert(towerSocket != null && towerSocket.SocketIsFree);
        Debug.Assert(selectedBuildingPrefab != null);

        Vector3 buildingPosition = towerSocket.TowerPosition;

        BuildingBase newTower = Instantiate(selectedBuildingPrefab, buildingPosition, Quaternion.identity);
[...]

The function itself is registered at a System.Action to be called, whenever I click on a position where the prefab can be instantiated. I don’t understand how this could cause this issue.
Will have to create a more isolated example for a bug report, otherwise I’d have to send half a project.

I have been able to reprocude my original issue (update before start) with this simple setup that mirrors my instantiation behaviour.
This is the output I get:
7556527--934468--upload_2021-10-8_10-11-35.png
edit: this order seems to be constant, i.e. it is always Awake x2, Start/Update, Start/Update

both DummyA and DummyB (has A replaced with B, no need to post the code twice)

public class DummyA : MonoBehaviour
{
    private bool _singleLog = true;

    private void Awake()
    {
        Debug.Log($"{name} - A - Awake"); # replace A with B for DummyB
    }

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log($"{name} - A - Start");# replace A with B for DummyB
    }

    // Update is called once per frame
    void Update()
    {
        if (_singleLog)
        {
            Debug.Log($"{name} - A - Update");# replace A with B for DummyB
            _singleLog = false;
        }
    }
}

the objects I click on in the game:

public class Clickable : MonoBehaviour
{
    public static Action<Clickable> OnClicked;

    private void OnMouseUpAsButton()
    {
        OnClicked?.Invoke(this);
    }
}

where the GOs are instantiated:

public class Instantiater : MonoBehaviour
{
    public GameObject Prefab;

    // Start is called before the first frame update
    void Start()
    {
        Clickable.OnClicked += SpawnPrefab;
    }

    private void SpawnPrefab(Clickable obj)
    {
        GameObject go = Instantiate(Prefab, obj.transform.position, Quaternion.identity);
    }
}

setup in scene

Great! Now I would check if the same problem present:

  • if you only call the Action without the OnMouseUpAsButton (then remove the click) - keep bug report as simple as possible
  • if you only use the OnMouseUpAsButton (remove the Action) - keep bug report as simple as possible

then submit it through the bug reporter tool

1 Like

yes, done that:
direct instantiation (without action) does the correct order (Awake x2, Start x2, Update x2)
and report has been sent

Resolved
This behaviour is as intended, and caused by triggering it from OnMouseUpAsButton. As this is called between Start and Update (Unity - Manual: Order of execution for event functions), it will call Start on each script right before it goes into the Update phase.