Wouldn’t it be better to subscribe to the events OnEnable() instead of doing it on Start()?
It depends.
Depends on how you want to handle unsubscribing.
If you unsubscribe OnDestroy() then it makes sense to subscribe on Start()
If you unsubscribe OnDisable() then it makes sense to subscribe OnEnable()
If you never destroy or disable the object, then OnEnable() and Start() are both called exactly just once so either works.
I was going for the approach of subscribing during OnEnable and unsubscribing OnDisable, but I may go back and change them all to Start() now because I am getting errors where OnEnable is trying to subscribe to an Instance whose Awake hasn’t been called yet.
I had thought all Awakes would run before all OnEnables, but apparently Awake() is run before OnEnable() within the same script before moving on to the next one. They are run together like a set.
edit: And now my action system is running it’s Start() events too early before it gets subscribers. How very annoying. Easy fix, I suppose, by adjusting the default state of the other script but I had hoped putting an event in start meant my UI just adjusted to the correct state on it’s own. I guess the lesson learned here is ‘Don’t invoke events on start.’
From what I’ve read, OnEnable is saved for things within the class that needs to be done while Start is for external references. That way, you prevent accessing something that isn’t ready to respond yet.
So: use OnEnable to initialize itself, use Start to initialize using other (from my reading)
Here’s a useful list of how each monobehavior method is called:
So when beginning, a script has Awake → OnEnable → Start you could organize your code to initialize internally on Awake, to initialize using external objects OnEnable, and then use Start to trigger initialization events. Just be careful that start only happens once.
a script has Awake → OnEnable → Start you could organize your code to initialize internally on Awake, to initialize using external objects OnEnable, and then use Start to trigger initialization events.
My emphasis on “to initialize using external objects OnEnable”.
This isn’t really true, unfortunately. At the beginning of a scene, for each script on a GameObject within the scene (not for dynamically created ones), Awake() and then OnEnable() are called before any other object’s Awake(). So, perhaps surprisingly, the ordering is actually (Awake then OnEnable) → Start. This means that it’s likely that not all objects have run their Awake yet when another object’s OnEnable runs - the only guarantee is that this object’s Awake has already run.
This means that it’s unsafe to reference anything outside of your own MonoBehaviour in your OnEnable function, as it may not be initialised yet, unless you can guarantee the script running order.
This is a particular problem with Singleton objects, because they often set themselves up in their own Awake, but this may not have executed at the time your OnEnable function runs.
This course deals with this by setting the Unity Script Execution Order to make sure the UnitActionSystem
runs before any other scripts, but I notice that this isn’t done for any of the other Singletons, so be wary (correction: this video covers this, albeit for a different problem, and proposes a different solution - I hadn’t finished this video when I wrote my original comment - but this new fix doesn’t address the Awake/OnEnable issue, if there is one with this class).
Generally Singletons are an anti-pattern - they are glorified global variables - but Unity makes it difficult to avoid them, as there’s no straightforward way to run initialisation code before Awake(). (There is a way using C# attributes but it’s messy). Perhaps a better way is to search the scene at initialisation for an object providing a specific component type and retain a reference to it, for use in Start or later.
Your original comment, “use OnEnable to initialize itself, use Start to initialize using other” is essentially correct. Once a Start function runs, all other in-scene objects have had their own Awake and OnEnable functions executed.
Incidentally, the use of Singletons typically breaks the handy “avoid domain reload” trick that speeds up entering Editor “Play Mode”, because static data members are not properly reinitialised. This can make the first play after a change work fine, but the second and subsequent one behave unpredictably because public static UnitActionSystem Instance
retains values from the last Play.
Perhaps these links are useful: