官术网_书友最值得收藏!

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 like 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 will initialize major subsystems, 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 telling which order this will happen in.

So, if we one set of objects that configure some data in their Awake() callback, and then another set of objects do something with that configured data in their own Awake() callback, some reorganization or recreation of Scene objects or a random change in the codebase 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().

There’s no way or 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, its 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() is called and just before its first Update(). Late-stage updates can also be done in the LateUpdate() callback.

If we’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().

Be warned that Unity's logger is notoriously expensive. Logging is unlikely to change the order of the callbacks, but it can cause some unwanted spikes in performance if used too aggressively. Be smart and do targeted logging only on the most relevant parts of the codebase.

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 and 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, then 60, followed by 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 something’s Update() being called a specific number of times, we will run into problems. It’s best to keep Coroutine 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 of a game-breaking bug that’s very hard to reproduce.

主站蜘蛛池模板: 绥中县| 虹口区| 三台县| 鸡东县| 平度市| 蓬莱市| 山东省| 佛山市| 应城市| 崇义县| 星子县| 建昌县| 阿合奇县| 浪卡子县| 巴楚县| 东台市| 周宁县| 肇东市| 玛多县| 邹城市| 呼伦贝尔市| 湘潭市| 日喀则市| 龙江县| 丹棱县| 阳曲县| 淳安县| 五指山市| 中超| 马山县| 临朐县| 雷波县| 天祝| 鹿邑县| 贺兰县| 广饶县| 双峰县| 尖扎县| 石嘴山市| 楚雄市| 科技|