The lifecycle of a Unity game object can be confusing. A few simple tips can make things clear and speed up your dev.
Awake vs. Start
Awake and Start are very similar functions. They’re two of the first few functions called when a script is activated, only called once, and are used to initialize the script. The difference between them is only in execution order. It goes Awake -> OnEnable -> Start. All Awake methods are called on a game object before all Start methods on the same object. Using the two methods for different purposes can be extremely useful.
Use Awake to initialize a script using only itself and its game object Use Start to further initialize based upon values in other scripts. For example, say we have a PlayerScript and a ShieldScript. When the player is at full health, a shield rotates around the player. If the PlayerScript loads all the stats for the player in Awake, then ShieldScript can assume the PlayerScript has the proper values during Start (Start comes after all Awake methods have been executed). If both scripts used Awake, the ShieldScript would have to ask if the PlayerScript has been initialized before reading its health value.
Having distinct purposes for each method is also useful as it sets a clear programming pattern. This enables you to make fewer decisions when coding, look up code faster, and generally speed up development. Just ask yourself the question: Does this code involved values from other scripts? If so, use Start, if not, use Awake.
One further caveat, some UI and screen measurements can be inaccurate during Awake. For this reason, put code referencing those values in Start. (You can GetComponent in Awake. However, the UI components size may not be scaled to the screen yet.) Another caveat, the Awake/Start order is only guaranteed for scripts activated at the same time. If you activate one game object and later activate another, the first game object may be fully initialized before the second one begins the process.
OnEnable is similar to Awake and Start, but it is called every time the script is activated. This makes it an ideal place for subscriptions or other initialization done on each activation. If you put subscriptions in OnEnable and unsubscribe during OnDisable, you can avoid extra code execution on unused objects.
You may have noticed a weakness with the above methods. None of them are called until the game object is activated. What if you want to have some behavior occur regardless of whether the object is activated or not? Say you have some subscription that should activate the object. When can you subscribe?
You can subscribe in the constructor. The constructor will always be called. However, it is important to note that certain UnityEngine methods are not available until an object has been activated and the initialization has taken place. GetComponent for instance will not work in the constructor. Keep this in mind.
And Their Opposites
Naturally, most of these initialization lifecycle methods have corresponding de-initialization methods. Awake and Start have OnDestroy, OnEnable has on Disable, and the constructor has the destructor. These methods are great places to do the corresponding clean-up to your initialization methods to keep your code and make it clear where to expect the code.
In summary, scripts should set themselves up in Awake, scripts should reference others and setup UI in Start, and scripts should do repeated setup or subscriptions in OnEnable. Minimize searching and unnecessary decisions by consistently following this paradigm and your code will be easier to use, debug, and share with others.
Bonus: Check out this super handy graph on the execution order in Unity.
Disagree with my thoughts? Have your own paradigms or use cases? Write a comment! We all have a lot to learn.