Search Unity

How to Avoid Execution Order Nightmares

Discussion in 'Scripting' started by skauth, Feb 14, 2018.

  1. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    Not really a question. Because I already have the answer. But in a recent project, I ran across several issues that seemed as though they could only be solved through using the Execution Order. But that's not best practice. It's fine for temporarily testing things, to see what's bugging out (though there are better ways for that, too). But you don't want your code to rely on Execution Order. As I've seen in another thread, "Relying on Execution Order probably means you have to much Coupling in your code" or something to that effect.

    So here's how you can make sure your objects are running their Update methods at the right time:

    Don't use Update. At least not on those classes whose order is vital. Instead, name your Update function something else, such as "Turn", or "Move", or "TakeTurn", or what ever makes sense in your scenario.

    Have a Manager class. This can be a singleton, but it doesn't have to be (avoiding Singletons is another story). I actually have like 6 different 'managers' which just contain variables; (and though this is mostly due to previous not knowing about the Header Attribute to divide sections in the inspector, the merit of avoiding that level of separation is debatable.) You take one of your managers, make sure it stores a list of the objects you have to update, in order. Then in the manager's Update method, call to the "Turn" or "Move" or "TakeTurn" method in each of your other objects.

    Example from my complicated thing:
    Code (CSharp):
    1. void FixedUpdate ()
    2. {
    3.     foreach (Out_Gate gate in outGates) {
    4.         gate.channel.isHigh = false;
    5.     }
    6.     foreach (In_Gate gate in inGates) {
    7.         gate.channel.isHigh = false;
    8.     }
    9.     foreach (Gate gate in gates) {
    10.         gate.state.Reset ();
    11.         gate.wasOn = gate.isOn;
    12.         gate.isOn = false;
    13.     }
    14.     foreach (In_Gate gate in inGates) {
    15.         gate.channel.Turn ();
    16.         gate.Turn ();
    17.     }
    18.     foreach (Out_Gate gate in outGates) {
    19.         gate.Turn ();
    20.         gate.channel.Turn ();
    21.     }
    22.     foreach (Gate gate in normalGates) {
    23.         gate.Turn ();
    24.     }
    25.     board.CurrentTick++;
    26.     if (board.CurrentTick == board.TotalTicks) {
    27.         EndMode ();
    28.     }
    29. }
    Well, I hope this helps someone in the future. Tell me if this is a good post or a bad one. Tell me if you agree or disagree.
     
  2. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Script Execution Order has nothing to do with coupling, just FYI- a class being tightly coupled or loosely coupled or not coupled at all doesn't change the fact that it's going to be accessed within the same frame, and may not be ready at that time.

    SEO is primarily used (I've found) for changing initialization order, rather than update order- Awake and Start are occasionally not enough steps in the initialization process to guarantee successful initialization of many classes that rely on eachother, without guaranteeing the order they're initialized in. When used in that way, there really aren't a lot of decent alternatives around, and there's nothing inherently evil about SEO to avoid using it there. However, when using it specifically for Update order, yes, there are indeed usually better ways to handle it.

    I wouldn't do what you've done though- I would use events, and/or a state machine. One of my biggest rules is that components should control themselves- here it looks like you're controlling all of the components from one place. I'm not fond of that- it means that any change of how those components are handled internally would need to be changed here as well. Instead, you may want to think about setting up an event on your controller which all of these child objects sign up for to get updates when the state changes. In that way, you can change your manager Update to just be something like:
    Code (CSharp):
    1. public class SomeManager : MonoBehaviour
    2. {
    3.     public enum GameState
    4.     {
    5.         State1,
    6.         State2,
    7.         State3
    8.     };
    9.  
    10.     public class StateChangeEvent : UnityEvent<GameState> { }
    11.  
    12.     // this is what your components register to
    13.     [System.NonSerialized]
    14.     public StateChangeEvent ManagerStateChange = new StateChangeEvent();
    15.  
    16.     private GameState _currentState = GameState.State1;
    17.     public GameState currentState
    18.     {
    19.         get { return _currentState; }
    20.         private set
    21.         {
    22.             if(_currentState != value)
    23.             {
    24.                 _currentState = value;
    25.                 ManagerStateChange.Invoke(_currentState);
    26.             }
    27.         }
    28.     }
    29.  
    30.     private void Update()
    31.     {
    32.         currentState = GameState.State1;
    33.         currentState = GameState.State2;
    34.         currentState = GameState.State3;
    35.     }
    36. }
    Now it's up to the child components to register themselves and unregister themselves as appropriate, and they can react to the state changes in any way they like. This is much preferable to micromanaging them externally IMO.
     
    Last edited: Feb 14, 2018
    phobos2077 and Kiwasi like this.
  3. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    You are right. That does seem a lot better, though it's slightly more complicated, and much more intimidating. For instance, how does a component register to a Unity Event? (Yes I'm going to go look that up)

    You're right, if I make a new class of component following the same pattern as one of my other classes. But if I change the pattern, I still have to update it here. For instance, if I made state1 go with objectA's turn, state2 go with objectB's turn, and then add an objectN that has to go between objectA and objectB, I have to make a new state. Especially if I've numbered them, I have to then rename state2, state3, and state4, which means editing all the other objects. And though there's the Refactor with F2 feature, it doesn't always work, and I realize it after I go back to Unity and there are build errors (realize that I haven't saved any of the other files that got changed.)

    But I'll just keep that in mind and I won't number them. I'll probably name the states something like Refresh, Input, Output, and GateSetup.

    Whelp... I'm going to go try and implement this now. (The code I posted is from a game I'm currently working on.)
     
  4. Fido789

    Fido789

    Joined:
    Feb 26, 2013
    Posts:
    343
    To be honest, I don't like the code you posted at all. There is very strong coupling between classes and you put all the game's code to one FixedUpdate method. You have exchanged execution order nightmare for maintenance nightmare.

    You should look at event aggregator pattern, it is very convenient to work with.
     
  5. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,838
    Personally I couldn't think of any reasonable way to avoid using script execution order.
    Consider you have a player, camera, grenade, and projectile path predictor of the grenade.
    The execution might need to occur in that order. The easiest way is to make use of Update vs LateUpdate, which in a sense is way to control the script execution order. But that might not be sufficient. So the next course of action might be to add the scripts in the SEC in that order. Problem solved in 15 seconds.
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    The system you decribed could be managed by a component that knows how to drive the logic. I mean, there'll be some interdependencies for sure, so you could as well make use of them.

    At some point the project grows larger, you'll realize it's becoming a mess if you solve problems that way. Especially if you keep adding more and more components to the list.
    It'll be a pain.
     
  7. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    It's actually pretty trivial to make a system to replace SEO for update calls.

    For instance, I have an event class type that allows for priority ordering, so listeners can register themselves at a given priority and be sure they're called sooner or later compared to other listeners (like how SEO works). By making a new MonoBehaviour singleton you can hook into the Update method and invoke the events from there, so that listeners are all called and updated in order. You can even create an "early update" by using a second MonoBehaviour to hook into Update first (use SEO to make that class have the greatest priority), etc... Lordofduct did this with his Spacepuppy Framework's GameLoop class (IIRC) if you need an example.

    This also lets you register for update calls from non-MonoBehaviour-derived classes, including static classes, and lets you register and unregister as needed without needing to use Enabled.

    You don't necessarily have to make it a singleton of course- by using a static class (with RuntimeInitializeOnLoad for initializing it) or a service provider instead for handling all of the logic, you can just create the GO with the MonoBehaviour hooks when needed from there, with DontDestroyOnLoad of course so they persist between scenes.

    That said, SEO is still the best way to handle initialization order IMO- that's primarily what I use it for, in the rare cases where it's necessary. As I said, Awake and Start are occasionally not enough steps to synchronize initialization between a group of codependent behaviours, so it's either use SEO and fix the problem easily in a few seconds, or create a group initialization method, either on one of the codependent components or using a manager class.
     
    Last edited: Feb 15, 2018
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    Agreed.

    Initialization order is the only place I use SEO.

    With one exception, and that is my GameLoop entry point class.
     
  9. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    I think I sometimes do what you (the OP) does, but in a lazier fashion. Many Updates() are rewritten as public doUpdate();. Then manually called by the player's Update. Typical code looks like:

    void Update() { gears.doUpdate(); gameBoard.doUpdate(); if(isUsingRain) rain.doUpdate(); ...

    You have to write out the order, but it's not too long and being able to see it right there is kind of nice. It can sometimes help with debugging. You can easily make different situations use different orders. I find IF'ing out certain updates (rain) easier and clearer than setting a bool in that class. And it's easy to rename a doUpdate back to Update if you realize there aren't any synch issues with that one.
     
  10. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    Okay, Lysander, I've been trying to figure out on my own how to use your Event Code. You've given me incomplete code. On each of the objects that listens to the main object sending out events, do I need to provide a switch statement for that argument? Is there any way to have it only listen to events with a particular value passed in, or do I have to provide an if or switch statement in every listener I add? So that means something like this, which means this delegate gets called 3 times per update even if it only does something 1 time. And that's per object that listens.
    Code (CSharp):
    1.  // Please note, Manager is a static class, which stores all my singletons, so like Manager.Prefab stores all my prefabs.  Manager.Graphic stores all my sprites.  Etc.
    2. public class Some_Gate {
    3.  
    4. void Start() {
    5.     Manager.Event.RunStateEvent.AddListener((Manager.Event.RunState state) => {
    6.         if (state == Manager.Event.RunStateEvent.Turn) {
    7.             Turn();
    8.         }
    9.     });
    10. }
    11. void Turn() {
    12.  
    13. }
    14. }
    I believe this makes a little more sense.
    Code (CSharp):
    1. public class EventManager {
    2.     UnityEvent StopEvent = new UnityEvent();
    3.     UnityEvent InputEvent = new UnityEvent();
    4.     UnityEvent OutputEvent = new UnityEvent();
    5.     UnityEvent TurnEvent = new UnityEvent();
    6.     UnityEvent StopEvent = new UnityEvent();
    7. }
    8. void Start() {
    9.     StartEvent.Invoke();
    10. }
    11. void Update() {
    12.     InputEvent.Invoke();
    13.     OutputEvent.Invoke();
    14.     TurnEvent.Invoke();
    15. }
    16. void Stop() {
    17.     StopEvent.Invoke();
    18. }
    19. }
    20.  
    21. public class Some_Gate {
    22. void Start() {
    23.     Manager.Event.TurnEvent.AddListener(Turn);
    24. }
    25. void Turn() {
    26.  
    27. }
    28. }
    It would make more sense to have a bunch of events like this, instead. Why would this be a bad idea?

    (Obviously, this is simplified, and those are not invoked directly from Update and Start. These particular ones are in RunMode, and I have RunStart() and RunUpdate() and RunStop(), which are called either from Update() or when myGameMode changes. Likewise, the FixedUpdate() from my earlier code was not the universal EventManager FixedUpdate.)
     
    Last edited: Feb 20, 2018
  11. I wouldn't use event-based system in Update. You're preparing your own headache for later.
    https://jacksondunstan.com/articles/3335

    If you run long enough you will kick off the GC.

    Events are for occasional use. If you will use parameters, use C# events, if you don't, and have 1-3 listeners, use C# events, if you don't use parameters, but you have more than 2-3 listeners, use UnityEvent.

    Do not use events in Update.

    ---
    My $0.02 for the original subject: You're writing a game. Games aren't OOP business logic. You will have to couple together a lot of things if you want to make it right. Do not be afraid to do so. It's not a Java Bookkeeping project. It's a game.
    Now, for obvious reasons, you want to avoid unnecessary coupling but you should know when you need to sacrifice the OOP and other pattern B.S. for better performance and/or simpler code (less code -> faster execution or less battery consumption).

    And a simple method call is much more faster than the over-abstracted OOP code-sculpture.
     
    Last edited by a moderator: Feb 21, 2018
    NotaNaN, vedram, Munchy2007 and 2 others like this.
  12. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    Oh my god. Thank you. I've been beating my head against the wall trying to make it 'right' while simultaneously trying to track everything down everywhere and change it all. It's so much harder to follow the flow of what's going on when using Events instead of simple calls.

    I think Events make more sense when the one object isn't aware of all the things going on, all the things it triggers. So like, the Player does something to perk the attention of enemies. The player object doesn't know about all the enemies. So the player object just triggers a public event. And all the enemy objects who know about the player can subscribe to that event. It doesn't make as much sense on something like my game, where there is a specific turn-based order to things.
     
  13. Also keep it loose as possible, include only the calls you really need. When you aren't developing a high-speed action game, usually frame-late actions aren't a problem. You can't even perceive, most of the time. So you really need to pull together the most important systems which are _really_ (I mean REALLY) rely on strict order. Most of the time they aren't.

    Also don't forget that games are not simulations. Everything is there to make it fun for the player(s). If the fuzzy execution order messes up the fun for the player, you take actions. If it does not: it does not matter. Don't try to be physically and logically accurate (99% of the time): seek the fun.
     
  14. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    Well it's a circuit-building puzzle game. The 1-tick delay between an input turning on and the gate opening up is a vital part of the game. It allows you to build oscillators which match a certain frequency, 1-tick pulsers, and other stuff, and it challenges you to not put too many gates into a single path. So accuracy does matter in this case.
     
  15. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    You can easily build a slow-tick system using slop-tastic coding.

    For example hooking special effects into the wait, do stuff, wait ... tick system might be a pain. So it's fine to run an "electricity travelling along a wire" coroutine fire-and-forget which should take 0.125 seconds, meanwhile the real "do next action" just waits that long. The electricity animation might end 1 frame before or after, but that might look better and it won't affect the real next tick. And that 0.125 seconds will really be 5 frames, or 4, but it doesn't matter as long as it looks good.
     
  16. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    After much, much thought. I've come to a new conclusion about coupling.

    When you have a manager, and you have small component classes (not specifically MonoBehavior components, but like... modules, classes that can have like 200 instances.), either the manager knows about the components, or the components know about the manager. There's not really a way to avoid that. One has to know about the other. However, they do not have to both know about each other.

    Using events, the components have to know about the manager. You cannot escape that. The manager holds the events. In order to subscribe to the events, the components have to know about it. This either means using a Singleton manager, or having a reference to the manager on every single one of those 200 components. Meanwhile, using a looping method call, the manager has to know about the components, but the components don't have to know about the manager.

    If your manager is constantly changing (which makes perfect sense if you're in the middle of building your game), then it is much easier for you to change how the components are referenced in the manager than it is to change where the events on the components refer to.

    Since the manager probably already knows about all the components (since the manager instantiated them in the first darn place, and those it doesn't it can still pick up easily), it makes more sense to have the manager know about the components than the components know about the manager. The event system doesn't make the code any less coupled than it already was. In fact, I'm pretty sure it makes it more coupled, because they then have to know about each other.

    I've seen a lot of hate on Singletons. And though I have the urge to defend them (mostly because I come from a Procedural Programming background), I have been trying to seek a workaround to them. Because Unit Testing sounds awesome. And the manager keeping track of all of its own pieces (as opposed to the pieces keeping track of the manager through a singleton) means that you could have 50 duplicate managers, each managing their own things, and there would be no conflict or confusion. I haven't done Unit Testing, but I would like to. The encouraged Singleton model makes it kinda hard to do, though.

    Now yes, when you have independent components, such as enemies and players and obstacles, you don't necessarily want the manager to control them so directly. But when these components are extremely logically-dependent anyways (segments of wire, logic gates, and timed inputs/outputs, etc), it is perfectly reasonable for them to be dependent script-wise. Not that it's okay to be loose and fast because it's just a game. But because some amount of coupling is non-optional, and if it was optional, Intellisense wouldn't exist because we'd never have to use it.
     
  17. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    That's pretty much exactly what Dependency Injection helps with. And while it can be complex, it doesn't have to be - you can set up a basic binding that works like a singleton, but with more versatility and the ability to do unit testing. I've been using Zenject recently and the learning curve was not difficult.

    For something like this, besides having a list in the manager or a reference in the individual parts, there's a third option - use the Transform hierarchy. While things like GetComponentsInChildren get a bad rep as they're often used in sloppy ways, the hierarchy is a legitimate part of your game's data and there's nothing wrong in using it as such if you're consistent.
     
    Last edited: Feb 26, 2018
  18. skauth

    skauth

    Joined:
    Jun 4, 2017
    Posts:
    17
    Yeah, GetComponent<> and the similar classes are fine. I actually rely on them quite a lot. They just can't be called all the time. I call them when it makes sense to do so (Start, Init, Reset), any time the component or list of components has changed, or if I'm only using it once. In fact, in my original code snippet, I was using `GetComponentsInChildren<Gate>()` at the beginning of the timed turns, to get all of the gates, and then sorting them out by the child classes In_Gate, Out_Gate, and other. Then during FixedUpdate(), I used the list that was stored. Of course, at that time I was also experimenting with making multiple GameMode classes, and attaching three different ones to a single GameObject, and turning the others off when it gets enabled. So that particular FixedUpdate() was only called about 160 times before it shut off. (Though it could be called more frequently in the more complicated levels I have yet to even think about. And when I rearranged the manager entirely (ended up scrapping all the GameMode classes), I was testing with only 80 ticks).

    Through rearranging my code for the events thing, I did end up cleaning up some stuff, and discovered that Out_Gate could be called at the same time as all the normal gates. Channel.Turn can be called from the normal Turn function. Or, alternatively... an approach I just discovered today is using SendMessage() BroadcastMessage(), which is honestly a lot more reasonable than events, because the Board object is never going to have objects within it that aren't designed to work the same way. Though I may have to rename the In_Gate.Turn() method. [Edit: Still not looking too great as a way of decoupling. And it doesn't work with intellisense. But better than subscribing to events attached to the manager. And it uses the hierarchy without calling GetComponent<>. I have no idea what the performance is...]

    At least one of my events is messed up still from the rearrange. The switch sprites (whether the input channel is sending signal or not) switches as appropriate. But the light sprites don't change color like they *were* doing before I made all the changes. I probably accidentally moved the `isHigh=false;` to the end of the FixedUpdate instead of the beginning. But because it's no longer centralized, I'm having trouble tracking it down. Oh joy.

    Speaking of which, I've stopped using the Collab feature and am going to try using GitHub (the desktop client version) because I can't select just part of a previous update with Collab. So when MonoDevelop glitched and saved the new file over the old file, or maybe renamed my old file to the new file as I was trying to open the new file, and then saved the different new file in its place. However it happened, I lost my MainManager file, which had the bulk of my code. And I couldn't get it back without reverting to a point I couldn't remember what I had been doing. (And no, it definitely was not in my recycling bin!)
     
    Last edited: Feb 27, 2018
  19. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    The primary problem is just the complexity of games, as well as how the approaches to develop such games have evolved. Any kind of entity system tends to make the entities be aware of just too many things around them. This is getting worse the larger a projects becomes, and that's a programmer's nightmare and a point at which everything can quickly go wrong or does not exactly work as it should (it might not even be noticeable)... I've seen that in a few projects, I've even implemented such parts by myself and I'm not happy about it when I take a look at them.
    Interact with this, handle that... oh! one more thing, just throw it in there, too, because it's so convenient...
    And suddenly, there are logical dependencies and order of execution issues you might need to fiddle with... At some point it's usually solved but as a tradeoff, everything might have gotten even worse, because there's often no proper application management.

    It's just that there's no "trivial main method". Even though everything runs on the main thread and therefore sequentially, it's still kinda unpredictable and hard to grasp what exactly could be the next state that your code and your entities have to deal with at runtime...
    That's also why I - as much as I admire the idea of DI frameworks - like to manually control much of the application flow, at least in games (or similar projects).

    I know, it's the nature of a complex game: A world of "living" and self-driven entities... Oh, great, but if you think about it - that's some huge load of complexity you simply cannot tackle so easily... You gotta get the chaos sorted in some way.

    Execution order problems in Awake + Start are usually easy to deal with, they're usually not the problem you should be afraid of, just use them as they're intended to be used.
    But there're other issues that are just waiting for you, and apparently this hasn't got alot of attention at all.
    You certainly need various subsystems that kind of handle your game logic step by step, allow you to evaluate certain state in a more predictable manner to avoid that "nightmare of execution order" in other places...Some of the practices that have proven to be efficient in usual application programming just don't get it right in games.
    For instance, one of the most trivial yet often flawed example is damage dealing & healing... You often read "put health checks into properties that change the health" in order to handle potential death of the character only when damage is dealt and so on...
    But now, think about it... Does it really make sense in a game? If you wanna be mor accurate, it doesn't make sense at all, because frames should be treated as snapshots and if you wanna get it right, you need to apply all changes that have occured since the last snapshot (but can still only be applied sequentially in Update-like methods) before any gameplay-critical decision is finally made.

    You won't find that very often though, it's a thing that's often completely ignored and perhaps it's even okay... but if you'd like to be more correct though, that's probably the biggest problem that you could run into with regards to execution order.
    In other words, if you managed to gather all the information that you may need in a particular frame first, you could solve the problem of not-yet-ready dependencies, logical flaws in game state evaluation etc...

    I'm currently trying to start re-structuring a huge project at my students job, so far it turns out really well and even made the components less aware about the application's logic and state... they can be simplified and generalized, since lesser dependencies increase the chance of components being less application-specific - which is great for various reasons.
     
    Last edited: Feb 27, 2018
  20. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    I think that's just the encapsulation rule. It's easier if don't think of properties as a category of their own, but as funny-looking functions. So it's another way of saying not to let everyone directly change the damage variable, hoping they remember to callcheckForDeath() afterwards.

    That's an old trick for faking simultaneousness, not just for games. Instead of having applyDamage(amt, source); actually apply the damage, you have it add [amt,source] to a list. Then you process the list later. It solves lots of problems, like giving the highest damage credit for the kill, but it's a pain. It's sort of the Command design pattern (sort of.)
     
    vedram likes this.
  21. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I know why it's done and it makes perfect sense to encapsulate such things in plenty situations, but it doesn't always make sense in games, in particular when the side-effects are crucial for the game state. I could have said methods instead of properties, that's not really relevant as it doesn't change anything at all - in the end properties are compiled into methods anyway.

    The point was that any mechanism that can be used by some entity to deal damage to some other entity should not have the potential side effect of an instant kill (in the middle of Update), as you cannot know (at this point in time) whether or not the entity will be dead at the end of the frame.
     
    Last edited: Feb 28, 2018
  22. phobos2077

    phobos2077

    Joined:
    Feb 10, 2018
    Posts:
    350
    Found myself adding all of my singleton Managers into a Script Execution Order list in a project settings. Got a feeling I'm doing something wrong. Found a solution to automatically set execution order via a custom Attribute and an editor script.. But could I avoid using it altogether?

    My code relies heavily on Observer pattern to reduce coupling. Also I've decided that Enabled state of my components should actually be respected. So naturally I've started to add listeners in OnEnable and remove them in OnDisable for all components that need to listen on other components.

    The problem is OnEnable is called right after Awake on that same component. It means I can't access Singleton-components in OnEnable to add listeners to them, because their Awake method could be called after OnEnable of the caller component.

    So I ended up adding my EventManager and GameManager to the SEO list. Which was fine until I started to add some more game systems that require global managers to work and UI controllers that need to present the state of those managers on the UI. So now every Manager component has to be added to SEO so it's Awake function is called before OnEnable of UI components.

    Basically this guy has very similar situation https://www.reddit.com/r/Unity3D/comments/9jgog2/unity_loads_scripts_in_wrong_order_when_using/

    Edit: did a simple test to illustrate the situation. As you can see Awake/OnEnable are called in pairs for every object. So I can't rely on another component's Awake being run in OnEnable unless I manually order their execution.


    Any directions or advises will be greatly appreciated.

    Edit: LOL, it appears I've fixed the problem without realizing it at first. Most of the problem came originally from the use of additive scene loading (some managers are separated from UI components that use them in different scene). But I actually solved it by making sure my scenes are loaded in the correct order. As long as my UI components are in the later or in the same scene, everything works.

    The only possible issue might be if in any of my OnEnable methods I will need some initialization result from Awake of another component. But as of now I mostly subscribe to events which works because C# objects themselves are all properly constructed before any Awake calls. I can reference singletons because of the way they're made (if Instance is requested before Awake is called, I use FindObjectByType<T> to get the instance - it works).
     
    Last edited: May 3, 2019