Welcome to another blog post about the stuff that quietly runs your entire game, whether you think about it or not. This Wednesday's topic: understanding the game loop in Unity; Awake, Start, Update, FixedUpdate, LateUpdate, OnEnable, OnDisable, Reset, teardown methods, coroutines, and a peek at the rendering callbacks.
If you've ever wondered why your player controller moves like it's had too much coffee on some frames and none on others, or why a script that "should" be running just... isn't, chances are you've got a game loop misunderstanding on your hands.
This stuff isn't glamorous, but it's foundational. Get it wrong, and every system you build on top of it inherits the wobble.
Let's have a closer look at how it all works together.
What Even Is the Unity Game Loop?
Unity's execution order is basically a giant conveyor belt of method calls that run every single frame. Your game doesn't just "happen"; Unity calls a very specific sequence of functions in a very specific order, over and over, dozens of times a second, until you close the game or your GPU gives up.
Understanding this order isn't optional trivia. It's the difference between "my code works" and "my code works, but only on Tuesdays, and only if the player object was enabled first."
Reset: The One That Only Fires in the Editor
Let's start with the oddball. Reset() doesn't run during gameplay at all; it fires in the Editor when you first attach a component to a GameObject, or when you right-click a component and hit "Reset." It's designed for setting sensible default values.
void Reset()
{
speed = 5f;
jumpHeight = 2f;
}
Use it to pre-fill fields so you (or a teammate) aren't staring at zeros the moment you drag a script onto an object. It's a small quality-of-life thing, but solo devs juggling a dozen systems will appreciate not having to remember every default from scratch.
Awake: The Method That Runs Before Anything Cares
Awake() is called once, when the script instance is loaded — before any Start() calls happen, and before the game technically "starts" doing anything interactive.
Use Awake() for:
- Setting up references between components on the same object
- Initializing variables other scripts might need immediately
- Singleton setup (the classic
if (instance == null) instance = this;dance)
Awake vs Start in Unity is a distinction beginners blur together, and it'll bite you. Awake() runs regardless of whether the component is enabled. Start() does not.
Start: The Actual Kickoff
Start() runs once too, but only if the script is enabled, and only right before the first frame's Update(). Unity guarantees all Awake() calls finish before any Start() calls begin — so Start() is the right home for initialization that depends on other objects already being awake.
The real answer to Awake vs Start in Unity: use Awake() for caching components, Start() for "get ready now that everyone else is ready too."
Update: Where Most Of The Chaos Lives
Update() runs once per frame — and frames aren't a fixed length of time. Your game might run at 144 FPS on a gaming rig and 30 FPS on your nephew's ancient laptop. Update() doesn't care; it fires whenever a frame renders.
Great for input handling, camera movement, anything visual that should feel snappy. Bad for physics, which brings us to the method everyone mixes up.
FixedUpdate: The Frame-Rate-Independent Sibling
Here's the Unity FixedUpdate vs Update debate in one sentence: Update() runs once per rendered frame, FixedUpdate() runs on a fixed timestep (default 0.02 seconds, configurable in Project Settings), decoupled from your frame rate.
Update() → tied to render frame rate → varies (30fps, 60fps, 144fps...)
FixedUpdate() → tied to fixed timestep → constant (default: every 0.02s)
Physics needs consistency. Move a Rigidbody inside Update() and your simulation becomes frame-rate dependent, meaning your game plays differently across hardware. Any Rigidbody.AddForce, Rigidbody.MovePosition, or physics-adjacent call belongs in FixedUpdate().
Rule of thumb for Unity FixedUpdate vs Update: visuals and input in Update(), physics in FixedUpdate().
LateUpdate: The One That Waits Its Turn
LateUpdate() runs once per frame too, but always after every Update() call has finished across every script. This ordering guarantee is the entire point.
Classic use case: camera follow logic. If your camera repositions in Update(), it might update before the player has finished moving that frame, causing a jittery, one-frame-behind camera. Put the camera logic in LateUpdate() instead, and it always runs after the player's already moved smooth tracking, no jitter.
Update() → player moves
LateUpdate() → camera catches up, now that player has already moved
Anything that needs to "react" to another object's final position for that frame (camera rigs, IK adjustments, UI that follows world-space objects) belongs here.
OnEnable And OnDisable: The Toggles Not Many Talk About Enough
OnEnable() fires every time a GameObject or component becomes active — not just once like Awake(). OnDisable() fires every time it's deactivated. Perfect for:
- Subscribing/unsubscribing to events (prevents memory leaks and the dreaded "why is this event firing on a destroyed object" error)
- Resetting state when an object is pooled and reused
- Pausing/resuming behavior without fully destroying anything
Gut-check: subscribing to a static event or UnityEvent? Use OnEnable/OnDisable. Using object pooling? OnEnable resets state, since Awake only fires once ever.
Coroutines: The Loop's Weird Cousin
Coroutines aren't technically part of the standard Unity execution order the way Awake/Start/Update are, but they run alongside it. A coroutine is a method that can pause execution and resume later — across multiple frames — using yield return.
IEnumerator DoSomethingLater()
{
yield return new WaitForSeconds(2f);
Debug.Log("Two seconds later, here I am.");
}
Great for fade effects, delayed spawns, cooldown timers — things that unfold over time without needing a full state machine. They're not threads; they still run on the main thread, just spread across frames. yield return null resumes on the next Update(); yield return new WaitForFixedUpdate() resumes right after the next FixedUpdate(). They're guests who show up at scheduled points in the loop, not a separate loop.
The Rendering Callbacks: OnBecameVisible, OnPreCull, and Friends
There's a whole second layer of the loop tied to rendering rather than gameplay logic, and it's worth knowing these exist even if you rarely touch them:
OnBecameVisible()/OnBecameInvisible()— fire when a Renderer enters or exits any camera's view. Handy for pausing expensive logic on objects nobody's currently looking at.OnPreCull()— called just before the camera culls the scene (decides what's visible). Useful for last-second visibility tweaks.OnPreRender()/OnPostRender()— bracket the actual camera rendering, useful for camera-specific effects.OnRenderObject()— called after regular rendering, good for custom GL drawing.
Solo devs won't live in these day-to-day, but they're the go-to when you need "only do this when the player can actually see it" optimizations, or custom camera effects that the standard loop doesn't cover.
OnDestroy and OnApplicationQuit: Cleaning Up After Yourself
OnDestroy() fires when a GameObject or component is destroyed; via Destroy(), scene unload, or the game closing. This is where you unsubscribe from events you didn't already handle in OnDisable, release resources, or save last-second state.
OnApplicationQuit() fires specifically when the application is shutting down (before OnDestroy calls ripple through). It's your last real chance to save data, close connections, or log analytics before everything goes dark.
Scene unload / Destroy() called → OnDisable() → OnDestroy()
App closing → OnApplicationQuit() → OnDisable() → OnDestroy()
Skip these and you'll eventually get null reference exceptions from event subscriptions that outlived the object that made them a classic "why does this only happen sometimes" bug.
So Which One Should You Actually Use?
- Setting default values in the Editor? Reset
- Setting up internal references? Awake
- Depends on other objects being ready? Start
- Input, camera, visual polish? Update
- Physics, Rigidbody movement? FixedUpdate
- Reacting to another object's final position this frame? LateUpdate
- Subscribing to events, resetting pooled objects? OnEnable / OnDisable
- Something unfolding over a few seconds without a full system behind it? Coroutine
- Optimizing based on camera visibility? OnBecameVisible / OnPreCull
- Cleanup before an object or the app dies? OnDestroy / OnApplicationQuit
None of these compete for the same job -- they're specialists in their own way. Bugs show up when one method does a job that belongs to another.
Wrapping Up
The Unity execution order isn't something you need to memorize perfectly on day one, but it's something you'll keep bumping into for as long as you make games, so it's worth getting comfortable with now rather than debugging your way into understanding it the hard way. Once Awake vs Start in Unity and Unity FixedUpdate vs Update stop being mysteries, a whole category of "weird" bugs just stops happening. That's a good trade for an afternoon of reading! Right?
No comments:
Post a Comment