Which of your methods Unity calls and in what order: why every Awake finishes before any Start, what belongs in each callback, and how the order explains most null-reference bugs a beginner hits.
Subject: Unity Game Engine · 61 slides · code lesson
Open the interactive version of this deck · Homework for this lesson
Title
Unity - Lesson 4
You never call these methods. Unity does - in an order worth knowing.
Objectives
You got the one lifecycle question right in the diagnostic. This lesson goes past it, because lesson 5 leans on the order: most 'it is null' errors are a lifecycle mistake wearing a null-reference costume.
Warm-up
Before anything on the next slides.
Discussion prompt
Name every method you can think of that Unity calls on a MonoBehaviour without you calling it. For each, say when you think it runs.
Hint: There are five you will use constantly and three more worth knowing.
Answer:
The five: Awake, Start, Update, FixedUpdate, LateUpdate.
The three: OnEnable, OnDisable, OnDestroy - the pair that brackets being switched off, and the one that runs when the object goes away.
If you named Update and Start only, that is completely normal. The other six are exactly where this lesson pays for itself.
Section
Part 1
Concept
Figure (svg): The MonoBehaviour lifecycle as a vertical timeline: Awake, OnEnable, Start, then a repeating frame loop, then OnDisable and OnDestroy
A MonoBehaviour is a component Unity knows how to talk to. If you write a method with one of a handful of exact names, Unity calls it at the matching moment. You never call them yourself.
Callback — A method you write and the engine calls. Unity finds them by exact name - which is why a typo is a silent failure rather than a compile error.
Unity Manual - Order of execution for event functions Order of execution
Intuition
Actors do not decide when to walk on. There is a call sheet: everyone in the building first, then everyone in costume, then the curtain goes up, then a scene runs over and over, then the curtain comes down.
Awake is arriving at the theatre. Start is the last check before the curtain. Update is the scene, running once per frame for as long as the show lasts. OnDestroy is leaving the building.
The useful part of the analogy: you do not ask an actor a question while they are still parking the car. That is what reaching for another object in Awake does.
Concept
| callback | when | what belongs there |
|---|---|---|
Awake | once, when the object is created | cache your own components, set your own fields |
OnEnable | after Awake, and on every re-enable | subscribe to events |
Start | once, before the first Update | find other objects and ask them things |
FixedUpdate | on the physics clock, ~50/second | forces, velocity, MovePosition |
Update | once per rendered frame | input, timers, game logic |
LateUpdate | after every Update this frame | camera follow, anything needing final positions |
OnDisable | on deactivate, and before OnDestroy | unsubscribe from events |
OnDestroy | when destroyed, or when Play ends | release anything nothing else will |
Learn the first three properly and the rest fall into place - they are all variations on 'when exactly'.
Worked example
Put this on two objects and the Console shows the global order, not just one object's story.
public class LifecycleLogger : MonoBehaviour
{
static int order; // shared by every instance
void Log(string callback)
{
order++;
Debug.Log($"{order,3} {callback,-12} on '{name}' frame {Time.frameCount}");
}
void Awake() { Log("Awake"); }
void OnEnable() { Log("OnEnable"); }
void Start() { Log("Start"); }
void Update() { Log("Update"); }
void OnDisable() { Log("OnDisable"); }
void OnDestroy() { Log("OnDestroy"); }
}The counter is static, on line 3
Why: One counter shared across every instance. That is what makes the ordering between objects visible - a per-object counter would show 1, 2, 3 on both and prove nothing.
| # | callback | object | frame |
|---|---|---|---|
| 1 | Awake | A | 1 |
| 2 | Awake | B | 1 |
| 3 | OnEnable | A | 1 |
| 4 | OnEnable | B | 1 |
| 5 | Start | A | 1 |
| 6 | Start | B | 1 |
| 7 | Update | A | 1 |
Prediction
Three objects, each with that logger, in one scene.
Predict first
Which order does Unity use?
Correct: Every Awake, then every OnEnable, then every Start.
Why: Unity runs the phases across all objects, not object by object. Which object goes first WITHIN a phase is not guaranteed - but the barrier between phases is, and that guarantee is the entire reason there are two setup callbacks instead of one.
Matching
Each of these lines of code belongs in exactly one callback.
Match the pairs
Where does each belong?
Why: Your own components in Awake; other objects in Start; physics on the physics clock; camera follow in LateUpdate so it sees the final positions of everything that moved this frame. Putting the camera in Update gives you a camera that lags one frame behind, which reads as jitter.
Trap
Start does my setup, so if I need to reset the object I will just call it again.
void OnPlayerRespawn()
{
Start(); // "re-run my setup"
}Callbacks are Unity's to call. If setup needs to be repeatable, put it in a method with a name that says so, and call that from Start.
void Start()
{
ResetForNewLife();
}
void OnPlayerRespawn()
{
ResetForNewLife(); // same code, deliberately, from both places
}
void ResetForNewLife() { health = 100; isDead = false; }| want | do |
|---|---|
| setup that runs once | put it in Awake or Start |
| setup that can repeat | a named method, called from the callback |
| setup on every re-enable | OnEnable - Unity already calls it every time |
Section
Part 2
Concept
Figure (svg): Two objects A and B showing all Awakes finishing before any Start begins
Every Awake in the scene finishes before any Start begins. So code in Awake cannot assume anyone else is ready - and code in Start can assume everyone is.
Build yourself in Awake. Reach for others in Start. That one line prevents the most common startup bug in Unity.
Worked example
Two objects, each wanting a value the other publishes in its own Awake.
public class WhyAwakeThenStart : MonoBehaviour
{
public WhyAwakeThenStart partner;
public int myNumber = 7;
public int Published { get; private set; }
void Awake()
{
Published = myNumber; // publish mine
int seen = partner.Published; // ...and read theirs. Too early.
}
}Line 9 always works
Why: You are writing your own field. Nothing else has to exist for that.
Line 10 works for exactly one of the two objects
Why: Whichever wakes second sees a published value. Whichever wakes first sees 0 - the default, because the partner has not reached line 9 yet.
| object | wakes | reads partner | gets |
|---|---|---|---|
| Left | first | Right.Published | 0 - wrong |
| Right | second | Left.Published | 7 - correct, by luck |
| Left (in Start instead) | - | Right.Published | 42 - correct, always |
| Right (in Start instead) | - | Left.Published | 7 - correct, always |
Anomaly
The lab prints this with both objects set to read in Awake.
Predict first
Both objects run identical code. Why does only one of them fail?
Correct: Whichever woke first read a partner that had not published yet.
[L04] PASS 'Pair · Right' read 7 from 'Pair · Left' during Awake and got lucky — it woke second
[L04] FAIL 'Pair · Left' read 0 from 'Pair · Right' during Awake, but the real value is 42Why: This is the shape of every script-execution-order bug: identical code, one succeeds, and which one succeeds can change between runs, between machines, and between a build and the editor. That is why the fix is never 'reorder the objects' - it is 'ask later'.
Explain it to yourself
Discussion prompt
The too-early read returned 0 rather than throwing an exception. Explain why that is the more dangerous outcome.
Answer:
Because nothing tells you. An exception names the file and the line; a 0 is a plausible number that flows on into the rest of the game and shows up as 'the enemy has no health' three systems later.
It is also intermittent: the object that woke second worked perfectly, so half your test cases pass. The rule 'read in Start' is cheap insurance against a bug that hides.
Concept
Edit > Project Settings > Script Execution Order lets you force some scripts to run their callbacks before others.
| question | answer |
|---|---|
| Does it work? | yes, reliably |
| Should you reach for it? | almost never |
| Why not? | it is invisible from the code - the next reader has no clue it exists |
| When is it right? | one global manager that genuinely must exist first, and even then Awake-then-Start usually solves it |
Treat needing it as a signal that something is being read too early. Ninety-nine times in a hundred, moving the read from Awake to Start is the real fix.
Discrimination
Sort each line by which callback it belongs in.
Sort into buckets
Which callback?
Fill the middle
Fill in the callback names so this script is set up safely.
Fill in the blanks
Rigidbody body;
GameManager manager;
void Awake()
Start
void ___()
___
Why: The split follows the ownership of what is being touched, not the complexity of the code. GetComponent on yourself is safe in Awake because you exist by definition; GameManager.Instance is only guaranteed to be set once the GameManager's own Awake has run, which the Start barrier guarantees.
Counterexample
Discussion prompt
Find a case where reading another object in Awake is genuinely safe. Then say what makes it safe, and why it is still a bad habit.
Hint: What if the other object was created by you, on the same line?
Answer:
If you Instantiate an object and then read it, its Awake has already run - Instantiate does not return until it has. So Instantiate(prefab).GetComponent<Enemy>().health = 50; is safe in Awake.
Why it is still a habit worth avoiding: the safety comes from a fact about that one line, not from the callback. Move the code, add a line, and the guarantee quietly disappears. Start is safe for a reason that survives editing.
Section
Part 3
Concept
A component's state decides which callbacks it gets. This is the table that explains 'my script stopped working and I never touched it'.
| state | Awake | OnEnable | Start | Update | OnDisable |
|---|---|---|---|---|---|
| normal | yes | yes | yes | yes | at the end |
| component checkbox off | yes | no | no | no | no |
| object inactive at load | no | no | no | no | no |
| object deactivated later | already ran | already ran | already ran | stops | yes |
The bold cell is the surprise: Awake runs on a disabled component. Awake is about the object being created, not about it running - which is why caching there is safe even for scripts that start switched off.
Prediction
You untick the checkbox beside a script - the component's own checkbox, not the object's - and press Play.
Predict first
Which callbacks run?
Correct: Awake only.
Why: Awake runs because the component exists. OnEnable, Start and Update do not, because it is not enabled. Tick the box later at runtime and OnEnable runs, followed by Start - Unity holds Start until the first frame the component is actually enabled.
Worked example
The lab's third cube switches itself off at t = 2s. Note what does NOT appear.
void Update()
{
if (!disabledYet && Time.timeSinceLevelLoad >= 2f)
{
disabledYet = true;
gameObject.SetActive(false); // OnDisable runs; OnDestroy does not
}
}| moment | callback | why |
|---|---|---|
| t = 0 | Awake, OnEnable, Start | normal startup |
| t = 0 to 2s | Update every frame | running |
| t = 2s | OnDisable | switched off - still in the scene |
| t = 2s onward | nothing | an inactive object runs no callbacks |
| you press Stop | OnDisable, then OnDestroy | the scene is torn down |
The last row is worth remembering: OnDisable always runs before OnDestroy. Anything you unsubscribe in OnDisable is already unsubscribed by the time OnDestroy runs.
Ranking
An object is created, deactivated at t = 2s, reactivated at t = 4s, then Play stops. Order the callbacks.
Put in order
Earliest first.
Why: Start appears exactly once in that list even though the object was enabled twice - Start is once per component, ever. OnEnable and OnDisable pair up any number of times, which is precisely why event subscription belongs in that pair and not in Start.
Trap
Set up once, tear down once. It reads symmetrically, so it must be right.
void Start() { GameEvents.OnScore += HandleScore; }
void OnDestroy() { GameEvents.OnScore -= HandleScore; }Subscribe in OnEnable, unsubscribe in OnDisable. They pair up every time, however many times the object is switched on and off.
void OnEnable() { GameEvents.OnScore += HandleScore; }
void OnDisable() { GameEvents.OnScore -= HandleScore; }| event | OnEnable/OnDisable | Start/OnDestroy |
|---|---|---|
| object deactivated | unsubscribes correctly | stays subscribed - bug |
| object reactivated | re-subscribes correctly | never re-subscribes |
| object destroyed | OnDisable runs first, so still correct | works |
Error analysis
A camera follow script. It compiles and it looks fine.
Annotate
Two lifecycle bugs in twelve lines, and neither is a compile error. Both fixes are one word: Awake becomes Start, Update becomes LateUpdate.
Comparison
Fill the blanks from the rules you now have.
Comparison matrix
| Runs on a disabled component? | Runs more than once? | Safe to touch other objects? | |
|---|---|---|---|
| Awake | yes | no | no |
| OnEnable | no | yes | usually |
| Start | no | no | yes |
Section
Part 4
Concept
Within a single frame Unity runs FixedUpdate (zero or more times), then Update, then LateUpdate. The order is fixed and it is the reason LateUpdate exists.
void FixedUpdate() { /* physics: forces, velocity, MovePosition */ }
void Update() { /* input, timers, game logic, non-physics */ }
void LateUpdate() { /* camera follow, anything needing finals */ }| callback | times per frame | clock |
|---|---|---|
FixedUpdate | 0, 1, 2 or more | physics: 0.02s per step |
Update | exactly 1 | frame: Time.deltaTime |
LateUpdate | exactly 1, after every Update | frame: Time.deltaTime |
Prediction
The row above says FixedUpdate may run zero times in a frame.
Predict first
When would a frame contain no FixedUpdate at all?
Correct: When the frame is shorter than the remaining physics step.
Why: At 144 fps a frame is about 0.007s, and the physics step is 0.02s - so roughly two frames in three contain no physics step. This is why 'once per frame' and 'once per step' are genuinely different rates in both directions, not just when the machine is struggling.
Worked example
The player moves in Update. The camera wants the position after that move.
// on the camera
void LateUpdate()
{
transform.position = target.position + offset;
}| camera code in | player moved this frame? | camera shows |
|---|---|---|
| Update, camera runs first | not yet | last frame's player position - one frame behind |
| Update, camera runs second | yes | correct, by luck of execution order |
| LateUpdate | yes, guaranteed | correct, always |
The symptom of getting it wrong is subtle: the camera does not break, it lags, and the player reads that as jitter or motion sickness rather than as a bug.
Invariant
Step through one frame at 144 fps and watch which counter changes.
Step through it
Which of these is constant across all three frames, and why does that matter?
Update and LateUpdate are exactly 1 per frame, always. FixedUpdate is whatever the clock says - so code that assumes 'one physics step per frame' is wrong in both directions.
Trade off
Weigh the two options for a piece of code that moves an object smoothly.
Comparison matrix
| In Update | In FixedUpdate | |
|---|---|---|
| Runs how often | once per frame | 50 times a second |
| Looks smooth | yes - matches the display | only with interpolation on |
| Collisions handled | no | yes |
| Frame-rate independent | only with Time.deltaTime | yes, inherently |
So: no Rigidbody, move in Update with Time.deltaTime. Rigidbody, move in FixedUpdate and set the body's Interpolate to Interpolate so the rendering smooths between steps.
Explain it
Discussion prompt
A teammate asks why their character 'stutters' when they move it in FixedUpdate. Answer in two sentences.
Answer:
Model answer: 'Physics updates 50 times a second but your screen draws 144, so the character's position only actually changes on some frames and the eye reads that as stutter.'
'Set the Rigidbody's Interpolate to Interpolate - it draws the body smoothly between physics steps without changing the simulation at all.'
Section
Build
Concept
A scene that prints its own lifecycle: three logged objects, one that switches itself off, and the two-object pair that fails when it reads too early.
Files: unity-labs/Assets/NaruhodoLabs/Lesson04_Lifecycle/.
Step zero
Discussion prompt
What is the smallest scene that proves 'every Awake finishes before any Start'? What must be true of the logging for the proof to hold?
Answer:
That is the difference between a log and evidence.
Worked example
Two cubes with the logger, then read the Console top to bottom.
// the line that does the work
Debug.Log($"{order,3} {callback,-12} on '{name}' frame {Time.frameCount}");| you should see | grouped how |
|---|---|
| Awake on A, Awake on B | both before any OnEnable |
| OnEnable on A, OnEnable on B | both before any Start |
| Start on A, Start on B | both before any Update |
| FixedUpdate, then Update, then LateUpdate | the frame order, every frame |
Which of A or B goes first inside a phase may differ on your machine. That is not a bug - it is exactly the thing you are not allowed to depend on.
Worked example
Both pair objects start with Read In Awake ticked. One line will be a FAIL.
void Awake()
{
Published = myNumber;
if (readInAwake) Check(partner.Published == partner.myNumber);
}
void Start()
{
if (!readInAwake) Check(partner.Published == partner.myNumber);
}| setting | Left reads | Right reads | result |
|---|---|---|---|
| Read In Awake ticked | 0 | 7 | one FAIL, one lucky PASS |
| Read In Awake unticked | 42 | 7 | two PASS, every run |
Untick the box on both and run again
Why: Nothing about the values changed. Only when the question was asked - which is the entire lesson in one experiment.
Hypothesis
You untick the object's checkbox on cube A (top of the Inspector) before pressing Play.
Predict first
How many of A's callbacks appear in the Console?
Correct: None at all.
Why: An inactive GameObject is not started up, so not even Awake runs. That is different from a disabled component, where Awake does run - and the difference is exactly the distinction between the object's switch and the component's switch from lesson 1.
Real world
Discussion prompt
Your game has a GameManager holding the score, a HUD that displays it, and 20 enemies that report kills to the manager. Where does each piece of setup go, and why?
Answer:
Notice the shape: every 'me' goes in Awake, every 'them' goes in Start, and every 'while I am switched on' goes in the OnEnable/OnDisable pair.
Concept
Six experiments from the lab notes.
| change | what happens |
|---|---|
| Untick the component checkbox before Play | Awake runs; Start and Update do not |
| Untick the object checkbox before Play | nothing runs at all, not even Awake |
| Re-enable an object that switched itself off | OnEnable runs again; Start does not |
Rename Start to start | it silently stops being called - no error |
| Add a second logger to the same object | both log; callbacks are per component |
Set Time.timeScale = 0 | Update keeps running, FixedUpdate stops |
Elimination
A script's Update never runs, and there are no errors in the Console.
Eliminate the wrong options
Which explanation should you check FIRST?
Survives elimination: A
Why: Check the cheapest and commonest cause first: two checkboxes, plus every parent in the Hierarchy. An inactive parent is the sneaky one, because the object's own checkbox is still ticked and activeSelf is still true - which is exactly what activeInHierarchy exists to tell you.
Notation
Four lines of real output. Every field is there for a reason.
Annotate
Definition probe
Some callbacks fire once in a component's whole life. Others fire every time a switch is flipped. Sorting them correctly is most of what you need.
Sort into buckets
Once per component ever, or repeatable?
Faded example
A patrolling enemy. Put each line in the right callback.
Fill in the blanks
Rigidbody body;
WaypointRoute route;
void Awake()
GetComponent}<Rigidbody>();
}
void Start()
FixedUpdate
void ___()
___
Why: Three callbacks, three kinds of work: my own component in Awake, another object's data in Start, and a physics move on the physics clock. Notice that route is read in Start rather than Awake because RouteManager.Instance is another object's field - and it is only guaranteed to be set once that manager's own Awake has run.
Missing information
Discussion prompt
A teammate says 'my script isn't running'. Before looking at their code, what are the four things you need to know - and what does each answer rule out?
Answer:
Those four questions land on four different rows of the state table, so whichever answer comes back non-obvious is the bug.
Pattern
The log so far reads: Awake on A, Awake on B, OnEnable on A, OnEnable on B.
Predict first
What is the next line?
Correct: Start on A (or on B - within a phase the order is not guaranteed).
Why: The phases are Awake, then OnEnable, then Start, then the frame loop. Both OnEnables have fired, so the OnEnable phase is complete and Start is next. If a third object existed, its Awake would have appeared before any OnEnable - so the log rules that out too.
Two truths and a lie
One is false. Rule it out and say what rules it out.
Eliminate the wrong options
Which claim about Awake is false?
Survives elimination: B
Why: Awake is once per component, ever. Reactivating fires OnEnable, not Awake - and not Start either. That is the whole reason the OnEnable/OnDisable pair exists: it is the only pair that tracks the switch rather than the lifetime.
Pattern
Four questions. They answer 'where does this code go?' every time.
Awake.Start.OnEnable, with the matching teardown in OnDisable.FixedUpdate, everything else in Update, and anything that must see this frame's final positions in LateUpdate.And one rule that catches the rest: if you are tempted to open Script Execution Order, you are reading something too early. Move the read to Start first.
Check
The question the diagnostic asked, and one step further.
Check your understanding
Two objects, A and B, each with a script defining Awake and Start. Which ordering is guaranteed by Unity?
Answer: B
Why: Unity runs the phases across all objects: every Awake, then every OnEnable, then every Start. Which object goes first inside a phase is not something to rely on, which is exactly why 'set up yourself in Awake, reach for others in Start' works.
Check
This one separates 'knows the list' from 'knows the rules'.
Check your understanding
A component's checkbox is unticked in the Inspector before you press Play. Which callbacks run on it?
Answer: B
Why: Awake runs because the component exists - Awake is about creation, not about running. OnEnable, Start and Update all require the component to be enabled. Tick the box at runtime and OnEnable fires, then Start, then Update from that frame on.
Socratic
Discussion prompt
Unity could have had a single Setup callback instead of Awake and Start. What would break?
Answer:
Ordering between objects. With one callback, object A's setup either runs before or after object B's - and whichever way round it is, half of all cross-references are reading something that has not been set yet.
Two phases with a barrier gives every object a slot where it is guaranteed to be alone with itself, and a later slot where everyone is guaranteed to be ready. That is not a Unity quirk - it is the standard two-phase initialisation pattern, and it appears anywhere objects have to find each other.
Check
The last one, and the most practical.
Check your understanding
Where should a script subscribe and unsubscribe from a global event?
Answer: B
Why: OnEnable and OnDisable pair up every time the object is switched on and off, so the subscription always matches the object's running state. They also cover destruction, because OnDisable always runs before OnDestroy.
Estimation
A game runs for 10 minutes at 60 fps.
Predict first
Roughly how many times does one object's Update run?
Correct: About 36,000 times.
Why: 60 x 60 x 10 = 36,000. That is why a GetComponent call in Update is worth caring about and one in Start is not, and why the habit is 'find it once, keep the reference'. Multiply by 200 objects and you are at seven million calls for something you could have done once.
Concept
Lesson 5 is the direct payoff: GetComponent in Awake, Instantiate in Start or Update, and Destroy with its end-of-frame delay all make sense only against this timeline.
| you now have | what it enables |
|---|---|
| the phase barrier | cross-object references that always work |
| OnEnable / OnDisable | subscriptions that survive being switched off |
| the frame order | camera follow and physics in the right places |
| the state table | diagnosing 'my script does not run' in seconds |
Analogy
If you have written a constructor and an init method, or used a page-load event, this is the same shape.
Match the pairs
Match each Unity callback to its closest cousin.
Why: The Awake-as-constructor mapping is close enough to be useful and worth one caveat: an actual C# constructor on a MonoBehaviour runs at a time Unity does not define, on the wrong thread for most engine calls, which is why Awake exists at all rather than you using the constructor you already know.
Connect it up
Draw it
Draw a vertical timeline for two objects side by side. Mark Awake, OnEnable, Start, the frame loop, OnDisable and OnDestroy, and draw the barrier line that both objects must cross before either may run Start.
Exit ticket
Predict first
Which is still shakiest?
Correct: Whichever you picked - the lab scene prints the answer for all four in one Play session.
Why: This topic tested as Probably OK, so the goal here is not repair but depth: lesson 5 assumes the timeline, and a shaky spot now becomes a confusing null reference then. Run the lab and read the numbered log against the item you picked.
Recap
You can say where any piece of setup belongs, and why, without guessing.
Awake runs even on a disabled component; nothing runs on an inactive objectOnEnable, unsubscribe in OnDisable - they pair up every timeFixedUpdate (zero or more), then Update, then LateUpdateLab: unity-labs/Assets/NaruhodoLabs/Lesson04_Lifecycle/SETUP.md. Run the pair experiment both ways before moving to lesson 5.
Want this taught 1-on-1? Alexander tutors Unity Game Engine — $55/session, free consultation.