Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

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

Discussion in 'Editor & General Support' started by RoHofmann, Oct 7, 2021.

  1. RoHofmann

    RoHofmann

    Joined:
    Aug 15, 2017
    Posts:
    15
    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:
    https://forum.unity.com/threads/start-before-update-except-when.32070/
    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?
     
    Last edited: Mar 13, 2022
  2. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,991
    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

    Code (CSharp):
    1. using UnityEngine;
    2. public class TestA : MonoBehaviour
    3. {
    4.     private int _c = 0;
    5.     private void Awake() => Debug.Log("Awake A");
    6.     private void Start() => Debug.Log("Start A");
    7.     private void Update()
    8.     {
    9.         if (_c != 0) return;
    10.         Debug.Log("Update A");
    11.         _c++;
    12.     }
    13. }
    TestB.cs

    Code (CSharp):
    1. using UnityEngine;
    2. public class TestB : MonoBehaviour
    3. {
    4.     private int _c;
    5.     private void Awake() => Debug.Log("Awake B");
    6.     private void Start() => Debug.Log("Start B");
    7.     private void Update()
    8.     {
    9.         if (_c != 0) return;
    10.         Debug.Log("Update B");
    11.         _c++;
    12.     }
    13. }
    Test.cs

    Code (CSharp):
    1. using UnityEngine;
    2. public class Test : MonoBehaviour
    3. {
    4.     [SerializeField] private GameObject prefab;
    5.     private void Update()
    6.     {
    7.         if (Input.GetKeyDown(KeyCode.A)) Instantiate(prefab, Vector3.zero, Quaternion.identity);
    8.         if (Input.GetKeyDown(KeyCode.S))
    9.         {
    10.             var go = new GameObject("bla")
    11.             {
    12.                     transform = { position = Vector3.zero }
    13.             };
    14.             go.AddComponent<TestA>();
    15.             go.AddComponent<TestB>();
    16.         }
    17.     }
    18. }
    Prefab

    screenshot1.png

    Results (no matter which one I execute, the results are the same):

    screenshot.png
     
  3. RoHofmann

    RoHofmann

    Joined:
    Aug 15, 2017
    Posts:
    15
    Thanks for your reply and test.

    I created a minimal example prefab to test. When I just instantiate very straight forward:
    Code (CSharp):
    1.             if (Input.GetMouseButtonDown(0))
    2.             {
    3.                 GameObject go = Instantiate(Prefab, Vector3.zero, Quaternion.identity);
    4.                 go.name = $"TestInstance - {index++}";
    5.             }
    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:

    Code (CSharp):
    1.     private void BuildTower(TowerSocket towerSocket, BuildingBase selectedBuildingPrefab)
    2.     {
    3.         Debug.Assert(towerSocket != null && towerSocket.SocketIsFree);
    4.         Debug.Assert(selectedBuildingPrefab != null);
    5.  
    6.         Vector3 buildingPosition = towerSocket.TowerPosition;
    7.  
    8.         BuildingBase newTower = Instantiate(selectedBuildingPrefab, buildingPosition, Quaternion.identity);
    9. [...]
    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.
     
  4. RoHofmann

    RoHofmann

    Joined:
    Aug 15, 2017
    Posts:
    15
    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:
    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)
    Code (CSharp):
    1. public class DummyA : MonoBehaviour
    2. {
    3.     private bool _singleLog = true;
    4.  
    5.     private void Awake()
    6.     {
    7.         Debug.Log($"{name} - A - Awake"); # replace A with B for DummyB
    8.     }
    9.  
    10.     // Start is called before the first frame update
    11.     void Start()
    12.     {
    13.         Debug.Log($"{name} - A - Start");# replace A with B for DummyB
    14.     }
    15.  
    16.     // Update is called once per frame
    17.     void Update()
    18.     {
    19.         if (_singleLog)
    20.         {
    21.             Debug.Log($"{name} - A - Update");# replace A with B for DummyB
    22.             _singleLog = false;
    23.         }
    24.     }
    25. }

    the objects I click on in the game:

    Code (CSharp):
    1. public class Clickable : MonoBehaviour
    2. {
    3.     public static Action<Clickable> OnClicked;
    4.  
    5.     private void OnMouseUpAsButton()
    6.     {
    7.         OnClicked?.Invoke(this);
    8.     }
    9. }
    where the GOs are instantiated:

    Code (CSharp):
    1. public class Instantiater : MonoBehaviour
    2. {
    3.     public GameObject Prefab;
    4.  
    5.     // Start is called before the first frame update
    6.     void Start()
    7.     {
    8.         Clickable.OnClicked += SpawnPrefab;
    9.     }
    10.  
    11.     private void SpawnPrefab(Clickable obj)
    12.     {
    13.         GameObject go = Instantiate(Prefab, obj.transform.position, Quaternion.identity);
    14.     }
    15. }

    setup in scene
    upload_2021-10-8_10-10-33.png
     
  5. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,991
    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
     
    RoHofmann likes this.
  6. RoHofmann

    RoHofmann

    Joined:
    Aug 15, 2017
    Posts:
    15
    yes, done that:
    direct instantiation (without action) does the correct order (Awake x2, Start x2, Update x2)
    and report has been sent
     
  7. RoHofmann

    RoHofmann

    Joined:
    Aug 15, 2017
    Posts:
    15