
Verifying the order of events
Unity applications mostly operate as a series of callbacks from Native code to Managed code. This concept will be explained in more detail in Chapter 8, Masterful Memory Management, but for the sake of a brief summary, Unity's main thread doesn't operate as a simple console application would. In such applications, code would be executed with some obvious starting point (usually a main() function), and we would then have direct control of the game engine, where we initialize major subsystems, and then the game runs in a big while loop (often called the game loop) that checks for user input, updates the game, renders the current scene, and repeats. This loop only exits once the player chooses to quit the game.
Instead, Unity handles the game loop for us, and we expect callbacks such as Awake(), Start(), Update(), and FixedUpdate() to be called at specific moments. The big difference is that we don't have fine-grained control over the order in which events of the same type are called. When a new scene is loaded (whether it's the first scene of the game or a later scene), every MonoBehaviour component's Awake() callback gets called, but there's no way of predicting the order in which this will happen.
So, if we take one set of objects that configure some data in their Awake() callback, and then another set of objects does something with that configured data in its own Awake() callback, some reorganization or recreation of scene objects or a random change in the code base or compilation process (it's unclear what exactly causes it) may cause the order of these Awake() calls to change, and then the dependent objects will probably try to do things with data that wasn't initialized how we expected. The same goes for all other callbacks provided by MonoBehaviour components, such as Start() and Update().
In any sufficiently complex project, there's no way of telling the order in which the same type of callback gets called among a group of MonoBehaviour components, so we should be very careful not to assume that object callbacks are happening in a specific order. In fact, it is essential practice to never write code in a way that assumes these callbacks will need to be called in a certain order because it could break at any time.
A better place to handle late-stage initialization is in a MonoBehaviour component's Start() callback, which is always called after every object's Awake() callback is called and just before its first Update() call. Late-stage updates can also be done in the LateUpdate() callback.
If you're having trouble determining the actual order of events, then this is best handled by either step-through debugging with an IDE (MonoDevelop, Visual Studio, and so on) or by printing simple logging statements with Debug.Log().
Coroutines are typically used to script some sequence of events, and when they're triggered will depend on what yield types are being used. The most difficult and unpredictable type to debug is perhaps the WaitForSeconds yield type. The Unity Engine is non-deterministic, meaning that you'll get a slightly different behavior from one session to the next, even on the same hardware. For example, you might get 60 updates called during the first second of application runtime during one session, 59 in the next, and 62 in the one after that. In another session, you might get 61 updates in the first second, followed by 60, and then 59.
A variable number of Update() callbacks will be called between when the coroutine starts and when it ends, and so if the coroutine depends on the Update() function of something being called a specific number of times, we will run into problems. It's best to keep a coroutine's behavior dead simple and dependency-free of other behavior once it begins. Breaking this rule may be tempting, but it's essentially guaranteed that some future change is going to interact with the coroutine in an unexpected way, leading to a long, painful debugging session for a game-breaking bug that's very hard to reproduce.