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. Dismiss Notice

Is there a way to change the default execution order of MonoBehaviour methods?

Discussion in 'Scripting' started by pleasurehouse, Apr 23, 2021.

  1. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96
    According to what I am observing, the normal order of execution of the functions of each MonoBehaviour class is like this.

    Class1 : MonoBehaviour
    Awake() --> Start() -->UpDate() -->FixedUpdate()

    Class2 : MonoBehaviour
    Awake() --> Start() -->Update() -->FixedUpdate()


    I wonder if there is any way to configure it to be like this:
    Class1 --> Class2 --> Class3 --> Class4
    Awake() --> Awake() --> Awake() --> Awake()
    Start() --> Start() --> Start() --> Start()
    UpDate() --> UpDate() -->UpDate() -->UpDate()

    That is: I need all the Awake () of each Class to be executed before all the Start () of each class.

    Is it possible to do this?
    Thank you so much
     
    Last edited: Apr 23, 2021
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,741
    That's exactly what happens. If you're seeing something different, it's because the second one is being created in a later frame than the first. (Or I guess more specifically, the second one is being created during or after the Start() of the first object runs)
     
    pleasurehouse and Schneider21 like this.
  3. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    That is how it works.

    Could you provide an example of it working otherwise, where the Start method of one instance is being called before the Awake method of another?
     
    pleasurehouse likes this.
  4. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96

    Ok, I have this Clases;

    First I was calling the AddSender (IEvent e) function from the EventInstaller :: Awake () and everything was working fine (except for a problem I had with clones) ...

    So I decided to put the AddSender (IEvent e) function inside EventManager :: Awake () but now nothing works ...

    It make me think that the order of execution was as I said above. But if not, I don't know what to think. Any idea what it could be?

    Code (CSharp):
    1.  
    2.  
    3. public class EventInstaller : MonoBehaviour
    4. {
    5.     List<IEvent> iEvent = new List<IEvent>();
    6.     //--------------------------------------------------
    7.     public void AddSender(IEvent e)
    8.     {
    9.         iEvent.Add(e);
    10.     }
    11.     //--------------------------------------------------
    12.     public void RemoveSender(IEvent e)
    13.     {
    14.         iEvent.Remove(e);
    15.     }
    16.     //--------------------------------------------------
    17.     public void AddListener(IEventListener listener)
    18.     {
    19.         foreach (IEvent e in iEvent)
    20.         {
    21.             e.OnEvent += listener.Listen;                    
    22.         }
    23.     }
    24.     //--------------------------------------------------
    25.     public void RemoveListener(IEventListener listener)
    26.     {
    27.         foreach (IEvent e in iEvent)
    28.         {
    29.             e.OnEvent -= listener.Listen;
    30.         }
    31.     }
    32. }
    33. //--------------------------------------------------------------
    34.  


    Code (CSharp):
    1.  
    2.     public class EventManager : EventListener, IEvent, IEventInfo
    3.     {
    4.         public event Action<EventInfo, EntityEvent.Type, EntityEvent.Property> OnEvent;
    5.         //--------------------------------------------
    6.         public virtual void Awake()
    7.         {
    8.             eventInstaller.AddSender(this);
    9.         }
    10.         //--------------------------------------------
    11.         public override void OnDestroy()
    12.         {
    13.             eventInstaller.RemoveSender(this);
    14.             base.OnDestroy();
    15.         }
    16.         //--------------------------------------------
    17.         public override void Start()
    18.         {  
    19.             base.Start();
    20.         }
    21.         //--------------------------------------------
    22.         public void SendEvent(EventInfo info, EntityEvent.Type eventType, Property property)
    23.         {
    24.             OnEvent?.Invoke(info, eventType, property);
    25.         }    
    26.     }
    27.  

    Code (CSharp):
    1.  
    2.     public class EventListener : MonoBehaviour, IEventListener
    3.     {
    4.         public EventInstaller eventInstaller;
    5.         //--------------------------------------------------
    6.         public virtual void Start() => eventInstaller.AddListener(this);
    7.         //--------------------------------------------------
    8.         public virtual void OnDestroy() => eventInstaller.RemoveListener(this);
    9.         //--------------------------------------------------
    10.         public virtual void Listen(EventInfo receiveInfo, EntityEvent.Type eventType, Property property)
    11.         {  
    12.         }
    13.      }
    14.  
    this was my problem with the clones:
    https://forum.unity.com/threads/how...its-clone-in-the-editor.1098076/#post-7069882

    thank you so much
     
    Last edited: Apr 23, 2021
  5. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    So your Awake methods are just adding a bunch of event listeners, right? That's fine. I think the issue is that once your last MonoBehavior calls its Awake method, Unity is just going ahead with running the scene's Start methods, but that doesn't mean your EventInstallers have all finished their subscriptions.

    However, I'm not clear on what kind of situation would cause a problem there. Are you expecting the code that responds to those event listeners to fire before Start does? Because that's not gonna happen. You might just be a way more advanced programmer than me, but I can't envision what you're doing here that needs to be done this way. Is it some kind of object pooling system?
     
  6. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96

    So your Awake methods are just adding a bunch of event listeners, right?
    Yes, That is.

    Are you expecting the code that responds to those event listeners to fire before Start does?
    No, i need it work after Start.

    You might just be a way more advanced programmer than me.
    I'm not... I'm learned C in the university 20 years ago... then i learned C++ by my self... and now i learning C# and dessing patterns... thats all i know.. I'm a chemical engineer ... but I would have liked to study computer engineering. XD

    Is it some kind of object pooling system?
    I'm using the events for communication among objects. They are really useful and i think all is more easy with them. I use it for almost everything.


    The last thing I'm doing is a damage system. I want a server to be in charge of managing the damage of all the characters. A single code for all objects.


    The Server:

    Code (CSharp):
    1.  
    2. //-------------------------------------------------
    3. using System;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using EntityEvent;
    7. //-------------------------------------------------
    8. namespace healthsystem
    9. {
    10.  
    11.     //-------------------------------------------------
    12.     [Serializable]
    13.     public struct Damagable
    14.     {
    15.         public Transform objectID;    
    16.         public HealthData healthData;
    17.     }  
    18.     //-------------------------------------------------
    19.     public class HealthServer : EventManager
    20.     {
    21.         [Tag] public string playerTag;
    22.         [SerializeField] private Damagable[] clientsList;
    23.         Dictionary<Transform,HealthData> clientsMap;
    24.  
    25.         //--------------------------------------------------
    26.         public override void Start()
    27.         {
    28.             base.Start();
    29.             clientsMap = new Dictionary<Transform, HealthData>();
    30.          
    31.             foreach (var clients in clientsList)
    32.             {
    33.                 if (clients.healthData.useClon)
    34.                 {
    35.                     clientsMap.Add(clients.objectID, clients.healthData.Clone());
    36.                 }
    37.                 else
    38.                 {
    39.                     clientsMap.Add(clients.objectID, clients.healthData);
    40.                 }
    41.             }        
    42.  
    43.         }    
    44.  
    45.         //--------------------------------------------------
    46.         public override void OnGameStart(EventInfo receiveInfo)
    47.         {
    48.             foreach (var clients in clientsList)
    49.             {
    50.                 clientsMap[clients.objectID].enableDamage = true;
    51.                 eventData.Lives = clientsMap[clients.objectID].totalHeath;//clientsMap[clients.objectID].currentLive;  
    52.                 SendEvent(new EventInfo(clients.objectID, this), EntityEvent.Type.lives, EntityEvent.Property.update);      
    53.             }
    54.         }
    55.         //--------------------------------------------------
    56.         public override void OnRespawnEnd(EventInfo receiveInfo)
    57.         {        
    58.             foreach (var clients in clientsList)
    59.             {
    60.                 clientsMap[clients.objectID].enableDamage = true;
    61.             }
    62.         }
    63.         //--------------------------------------------------
    64.         public override void OnHealthGet(EventInfo receiveInfo)
    65.         {
    66.             if (clientsMap[receiveInfo.sender].curentHeath<clientsMap[receiveInfo.sender].totalHeath)
    67.             {
    68.                 clientsMap[receiveInfo.sender].curentHeath += receiveInfo.GetHealth();
    69.              
    70.                 sentInfo.sender = receiveInfo.sender;
    71.                 eventData.Health = clientsMap[receiveInfo.sender].curentHeath;
    72.                 SendEvent(EntityEvent.Type.health, EntityEvent.Property.update, receiveInfo);
    73.             }
    74.         }
    75.         //--------------------------------------------------
    76.         public override void OnHealthLost(EventInfo receiveInfo)
    77.         {
    78.  
    79.             //acutualizar el nivel de salud
    80.             if (clientsMap[receiveInfo.sender].enableDamage && clientsMap[receiveInfo.sender].curentHeath > 0)
    81.             {
    82.                 eventData.Health = clientsMap[receiveInfo.sender].curentHeath -= receiveInfo.GetDamage();
    83.                 SendEvent( new EventInfo(receiveInfo.sender, this), EntityEvent.Type.health, EntityEvent.Property.update);
    84.             }
    85.  
    86.             //se ha perdido una vida (restablecer el nivel de salud al maximo)
    87.             if (clientsMap[receiveInfo.sender].curentHeath <= 0)
    88.             {
    89.                 clientsMap[receiveInfo.sender].enableDamage = false;
    90.                 SendEvent(new EventInfo(receiveInfo.sender),EntityEvent.Type.lives, EntityEvent.Property.lost);
    91.  
    92.                 eventData.Health = clientsMap[receiveInfo.sender].curentHeath = clientsMap[receiveInfo.sender].totalHeath;
    93.                 SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.health, EntityEvent.Property.update);
    94.             }
    95.          
    96.          
    97.             Debug.Log(this + " -->" + receiveInfo.sender.tag  +
    98.                       " curentHeath-->" +  clientsMap[receiveInfo.sender].curentHeath +
    99.                       " currentLive-->" +  clientsMap[receiveInfo.sender].currentLive +
    100.                       " enableDamage-->" +  clientsMap[receiveInfo.sender].enableDamage);
    101.          
    102.          
    103.          
    104.         }
    105.  
    106.  
    107.         //--------------------------------------------------
    108.         public override void OnLivesLost(EventInfo receiveInfo)
    109.         {
    110.             if (clientsMap[receiveInfo.sender].currentLive > 0)
    111.             {
    112.                 //clientsMap[receiveInfo.sender].enableDamage = true;
    113.              
    114.                 eventData.Lives = clientsMap[receiveInfo.sender].currentLive -= 1;
    115.                 SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.lives, EntityEvent.Property.update);
    116.                 SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.dead, EntityEvent.Property.start);
    117.             }
    118.             if (clientsMap[receiveInfo.sender].currentLive <= 0)
    119.             {
    120.                 //clientsMap[receiveInfo.sender].enableDamage = false;
    121.              
    122.                 if (receiveInfo.sender.CompareTag(playerTag))
    123.                 {
    124.                     SendEvent(new EventInfo(receiveInfo.sender, this), EntityEvent.Type.game, EntityEvent.Property.end);
    125.                 }            
    126.             }        
    127.      
    128.         }
    129.         //--------------------------------------------------
    130.         public override void OnLivesGet(EventInfo receiveInfo)
    131.         {
    132.             if (clientsMap[receiveInfo.sender].currentLive  < clientsMap[receiveInfo.sender].totalLives )
    133.             {
    134.                 eventData.Lives = clientsMap[receiveInfo.sender].currentLive += receiveInfo.GetLives();
    135.                 SendEvent(EntityEvent.Type.lives, EntityEvent.Property.update, receiveInfo);
    136.             }
    137.         }    
    138.      
    139.      
    140.      
    141.     }
    142. }
    143.  

    The Client:

    Code (CSharp):
    1.  
    2. //-------------------------------------------------
    3. using UnityEngine;
    4. using EntityEvent;
    5. //-------------------------------------------------
    6. namespace healthsystem
    7. {
    8.     public class HealthClient : EventManager
    9.     {
    10.         public HitersDataBase hitersDataBase;
    11.         private int count;
    12.         private bool isHurt;
    13.         //-------------------------------------------------
    14.         public bool CheckHiters(Collider2D collision) => hitersDataBase.hiters.CheckHiters(collision);
    15.         //-------------------------------------------------
    16.         bool IsWeapon(Collider2D collision)
    17.         {
    18.             if (collision.gameObject.TryGetComponent(out IWeapon iWeapon))
    19.             {  
    20.                 if (iWeapon.OwnerTag == "none" || iWeapon.OwnerTag == transform.tag)
    21.                 {      
    22.                     cancelCheckCollision = true;
    23.                     return true;
    24.                 }
    25.             }
    26.             return false;
    27.         }
    28.         //-------------------------------------------------
    29.         bool IsHitter(Collider2D collision)
    30.         {
    31.             if (!isHurt && CheckHiters(collision))//damage
    32.             {
    33.                 isHurt = true;
    34.                 count = 0;
    35.    
    36.                 if (collision.gameObject.TryGetComponent(out IEventInfo iEventInfo))
    37.                 {
    38.                     Debug.Log(this + " Hit Event-->" + collision.gameObject.tag + " -->" + iEventInfo.EventData().eventType + " -->" + iEventInfo.EventData().eventProperty);
    39.                     SendEvent(new EventInfo(transform, iEventInfo, collision), EntityEvent.Type.health, EntityEvent.Property.lost);
    40.                 }
    41.                 return true;
    42.             }
    43.             return false;
    44.         }
    45.         //-------------------------------------------------
    46.         void GetBonus(Collider2D collision)
    47.         {    
    48.             if (collision.gameObject.TryGetComponent(out IEventInfo iEventInfo))
    49.             {
    50.                 //Debug.Log(this  + "  otro evento-->" + collision.gameObject.tag + " -->" + iEventInfo.EventData().eventType + " -->" + iEventInfo.EventData().eventProperty);
    51.                 SendEvent(new EventInfo(transform,iEventInfo, collision), iEventInfo.EventData().eventType, iEventInfo.EventData().eventProperty);
    52.             }        
    53.         }
    54.  
    55.         //-------------------------------------------------  
    56.         public virtual void OnTriggerEnter2D(Collider2D collision)
    57.         {
    58.             if (IsWeapon(collision)) return;      
    59.             if (IsHitter(collision)) return;
    60.             GetBonus(collision);
    61.         }
    62.  
    63.         //-------------------------------------------------
    64.         public virtual void OnTriggerExit2D(Collider2D collision)
    65.         {  
    66.             if (isHurt && CheckHiters(collision))
    67.             {
    68.                 isHurt = false;
    69.             }
    70.         }
    71.         //-------------------------------------------------
    72.         public void Update()
    73.         {
    74.             if (isHurt && count == 1) isHurt = false;
    75.  
    76.             if (int.MaxValue == count) count = 2;
    77.             count++;
    78.         }
    79.         //-------------------------------------------------  
    80.     }
    81. }
    82.  

    It's not finished yet. But more or less you can see what I'm trying to do.
    What do you think about the idea?
     
    Last edited: Apr 23, 2021
  7. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96

    Look this video i made... i found that there is some Awake() functions are executing after Start() function... it is a clon...it is the clons fault... they are created after the others so the Awake funtion have a delay... i dont know what can i do to solve this problem ¿some idea?

     
    Last edited: Apr 23, 2021
  8. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96

    Ok, get it to work !! I only had to make a few small changes in the EventInstaller ... Since the Awake () did not work for me, I had to transfer the responsibility to Update ().

    The code is like this:


    Code (CSharp):
    1.  
    2. //--------------------------------------------------
    3. public class EventInstaller : MonoBehaviour
    4. {
    5.     List<IEvent> iEvent = new List<IEvent>();
    6.     private bool isEventAdded;
    7.    
    8.     private List<IEventListener> listeners = new List<IEventListener>();
    9.     //--------------------------------------------------
    10.     public void AddSender(IEvent sender)
    11.     {
    12.         if (!iEvent.Contains(sender))
    13.         {
    14.             iEvent.Add(sender);
    15.             isEventAdded = true;
    16.         }
    17.     }
    18.     //--------------------------------------------------
    19.     public void RemoveSender(IEvent sender)
    20.     {
    21.         if (iEvent.Contains(sender))
    22.         {
    23.             iEvent.Remove(sender);
    24.         }
    25.     }
    26.     //--------------------------------------------------
    27.     public void AddListener(IEventListener listener)
    28.     {
    29.         listeners.Add(listener);
    30.        
    31.         foreach (IEvent sender in iEvent)
    32.         {
    33.             sender.OnEvent += listener.Listen;                      
    34.         }
    35.     }
    36.     //--------------------------------------------------
    37.     public void RemoveListener(IEventListener listener)
    38.     {
    39.         listeners.Remove(listener);
    40.        
    41.         foreach (IEvent sender in iEvent)
    42.         {
    43.             sender.OnEvent -= listener.Listen;
    44.         }
    45.     }
    46.      //--------------------------------------------------
    47.     public void RemoveAllSubscriptions()
    48.     {
    49.         foreach (IEvent e in iEvent)
    50.         {
    51.             foreach (IEventListener listener in listeners)
    52.             {
    53.                 e.OnEvent -= listener.Listen;
    54.             }
    55.         }
    56.     }
    57.       //--------------------------------------------------
    58.     public void UpdateSubscriptions()
    59.     {
    60.         foreach (IEvent e in iEvent)
    61.         {
    62.             foreach (IEventListener listener in listeners)
    63.             {
    64.                 e.OnEvent += listener.Listen;
    65.             }
    66.         }
    67.     }
    68.     //--------------------------------------------------
    69.     void Update()
    70.     {  
    71.         if (isEventAdded)
    72.         {
    73.             RemoveAllSubscriptions();
    74.             UpdateSubscriptions();
    75.             isEventAdded = false;
    76.         }
    77.     }
    78. }
    79. //--------------------------------------------------------------
    80.  
    I no longer have any problem with clones ... life is wonderful !! XD

    Thank you very much for your help !! very appreciated!!
     
  9. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    Why dont you just make one class what will handle this runtime?
     
  10. MDADigital

    MDADigital

    Joined:
    Apr 18, 2020
    Posts:
    2,198
    I haven't looked at what you want to achieve so there might be better solutions.


    But as a rule of thumb. Never have external dependencies from Awake.

    In Awake you setup your internal stuff. Then in Start you setup external dependencies. This way you make sure all external dependencies have executed their Awake which setup up event bus, singleton or how you choose to communicate between components.
     
  11. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96
  12. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96

    That sounds consistent. I will do so. Thank you very much for the advice.
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,529
    pleasurehouse likes this.
  14. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96
    Thank you so much MelvMay!!
     
  15. pleasurehouse

    pleasurehouse

    Joined:
    Oct 15, 2020
    Posts:
    96
    It wasn't necessary ... I just had to restructure my code ... it was a cloning problem !!
     
    MartinMa_ likes this.