Topic 4 of 15 12 min beginner

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

MethodWhenUse it for
Awake()Once, as the object loadsSet up this object. Cache components.
OnEnable()Every time it is switched onSubscribe to events.
Start()Once, before the first frameTalk to other objects.
FixedUpdate()Fixed timestep, 50x per secondPhysics and forces.
Update()Every rendered frameInput and general logic.
LateUpdate()After every UpdateCameras and follow logic.
OnDisable()Every time it is switched offUnsubscribe from events.
OnDestroy()Once, as it is destroyedFinal 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:

Use Awake to set yourself up. Use Start to reach for other objects — by then, everything else has finished its own setup.
C#
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);
    }
}
Reaching for a singleton in 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.

MethodRuns atPut this in it
UpdateFrame rate (varies)Input, timers, non-physics movement, animation triggers
FixedUpdate50x per second by defaultRigidbody forces, velocity, MovePosition
LateUpdateAfter all UpdatesCamera 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.

C#
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;
    }
}
Never read 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.

CameraFollow.cs
C#
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
    );
}
Rule of thumb: if your code reacts to where something ended up this frame, it belongs in 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.

C#
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.

Unity's default script template includes empty Start and Update methods. Deleting the ones you do not need is free performance and less noise.

What to take away

  • All Awake calls finish before any Start call begins.
  • Set yourself up in Awake; find other objects in Start.
  • Read input in Update, apply physics in FixedUpdate.
  • Follow cameras in LateUpdate to avoid judder.
  • Every OnEnable subscription needs an OnDisable unsubscribe.
  • Delete lifecycle methods you leave empty.