The MonoBehaviour Lifecycle
Awake, Start, Update, FixedUpdate
You never call your own MonoBehaviour methods to start the game. Unity calls them for you, in a fixed order, at fixed moments. Knowing that order is the difference between code that works and code that throws a null reference on the first frame.
The order, once
Called in this sequence
| Method | When | Use it for |
|---|---|---|
Awake() | Once, as the object loads | Set up this object. Cache components. |
OnEnable() | Every time it is switched on | Subscribe to events. |
Start() | Once, before the first frame | Talk to other objects. |
FixedUpdate() | Fixed timestep, 50x per second | Physics and forces. |
Update() | Every rendered frame | Input and general logic. |
LateUpdate() | After every Update | Cameras and follow logic. |
OnDisable() | Every time it is switched off | Unsubscribe from events. |
OnDestroy() | Once, as it is destroyed | Final cleanup. |
Awake vs Start: the rule that matters
Both run once. The difference is timing across objects. Every object's Awake runs before any object's Start. That single fact gives you a dependable rule:
Awake to set yourself up. Use Start to reach for other objects — by then, everything else has finished its own setup.public class Player : MonoBehaviour
{
private Rigidbody rb;
private GameManager manager;
private void Awake()
{
// My own components. Safe - they exist with me.
rb = GetComponent<Rigidbody>();
}
private void Start()
{
// Someone else. Safe now - their Awake has already run.
manager = GameManager.Instance;
manager.RegisterPlayer(this);
}
}Awake is the classic first-frame null reference. If GameManager assigns Instance in its Awake and yours runs first, you get null. Move the lookup to Start.Update, FixedUpdate, LateUpdate
Picking the wrong one produces jitter that is maddening to debug, so the choice is worth understanding properly.
| Method | Runs at | Put this in it |
|---|---|---|
Update | Frame rate (varies) | Input, timers, non-physics movement, animation triggers |
FixedUpdate | 50x per second by default | Rigidbody forces, velocity, MovePosition |
LateUpdate | After all Updates | Camera follow, anything that must see final positions |
FixedUpdate may run zero, one, or several times in a single frame depending on how long that frame took. That is exactly why physics belongs there: it stays consistent regardless of frame rate.
private float horizontal;
private bool jumpQueued;
private void Update()
{
// Read input every frame so nothing is missed
horizontal = Input.GetAxisRaw("Horizontal");
// GetKeyDown is true for one frame only - buffer it
if (Input.GetKeyDown(KeyCode.Space))
jumpQueued = true;
}
private void FixedUpdate()
{
// Apply it on the physics step
rb.velocity = new Vector2(horizontal * speed, rb.velocity.y);
if (jumpQueued)
{
rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
jumpQueued = false;
}
}private void FixedUpdate()
{
// GetKeyDown is only true during the frame it happened.
// FixedUpdate does not run on every frame, so this
// silently swallows jumps. The player calls it "laggy".
if (Input.GetKeyDown(KeyCode.Space))
rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
}GetKeyDown or GetKeyUp inside FixedUpdate. Read input in Update, store it, and consume it in FixedUpdate.Why cameras belong in LateUpdate
If your camera follows the player in Update, you are racing: whichever script Unity happens to run first wins. Sometimes the camera moves before the player, so it renders a frame behind — visible as a subtle judder.
private void LateUpdate()
{
// Every Update has finished, so the player is where
// it will actually be drawn this frame.
Vector3 target = player.position + offset;
transform.position = Vector3.SmoothDamp(
transform.position, target, ref velocity, smoothTime
);
}LateUpdate.OnEnable and OnDisable come in pairs
These run every time the object is switched on or off, not just once. That makes them the correct place to subscribe and unsubscribe from events — and forgetting the unsubscribe is a genuine memory leak.
private void OnEnable()
{
GameEvents.OnPlayerDied += HandleDeath;
}
private void OnDisable()
{
// Always mirror the subscription, or the event keeps a
// reference to a destroyed object and calls into nothing.
GameEvents.OnPlayerDied -= HandleDeath;
}Execution order between scripts
Within a single phase, the order Unity runs different scripts is undefined. Do not rely on it. If two scripts genuinely must run in a set order, either restructure so they do not, or set it explicitly in Edit → Project Settings → Script Execution Order.
Prefer restructuring over the execution order setting
The Script Execution Order window works, but it is invisible: a new developer reading the code has no way to know an ordering dependency exists. It becomes a trap.
Usually the same problem is better solved with the Awake/Start split, an explicit initialisation call from a manager, or an event that fires when setup is genuinely finished. Reserve the setting for cases where you have no other option.
Performance: empty methods still cost
If a MonoBehaviour defines Update, Unity calls it across the engine boundary every frame — even if the body is empty. One is irrelevant; a thousand objects with empty Update methods is measurable. Delete lifecycle methods you are not using.
Start and Update methods. Deleting the ones you do not need is free performance and less noise.What to take away
- All
Awakecalls finish before anyStartcall begins. - Set yourself up in
Awake; find other objects inStart. - Read input in
Update, apply physics inFixedUpdate. - Follow cameras in
LateUpdateto avoid judder. - Every
OnEnablesubscription needs anOnDisableunsubscribe. - Delete lifecycle methods you leave empty.