Unity Term Book
Scripting & Lifecycle

MonoBehaviour

MonoBehaviour is the base class for every Unity script — it provides lifecycle hooks, Component access, and the bridge between C# code and the Unity Engine.

Imagine...

MonoBehaviour is like an employment contract with Unity. Once you sign (inherit MonoBehaviour), the employee (your script) is automatically invited to scheduled meetings: kickoff (Awake), first day (Start), daily standup (Update), physics sync (FixedUpdate), and exit interview (OnDestroy). No inheritance = never called, even if you define the methods.

The concept in detail

MonoBehaviour inherits Behaviour → Component → Object. That inheritance lets Unity invoke magic methods (Awake, Start, Update…) via reflection — you do not override or call a base method.

Key lifecycle: Awake runs as soon as the object is created (even if disabled), Start runs on the first frame after the object is enabled, Update every frame, FixedUpdate on the Physics timestep (default 0.02s).

Performance: an empty Update() still costs a reflection call. Delete empty Updates. Use enabled = false to pause the lifecycle instead of destroying the whole object.

Limits: you cannot use a constructor ( new MyScript()) — use AddComponent<T>(). Not thread-safe — MonoBehaviour APIs run on the main thread only.

Diagram: Inheritance chain & magic methods

Object
Component
Behaviour
MonoBehaviour
YourScript

Awake()

Internal init · Before Start

Start()

Initial setup · First frame

Update()

Per-frame logic · Input, AI...

FixedUpdate()

Physics · every 0.02s

LateUpdate()

After all Updates · Camera follow

OnEnable()

When the object is activated

OnDisable()

When the object is deactivated

OnDestroy()

When the object is destroyed

Hands-on steps

1

Create a new C# script

Assets → Create → C# Script. Match the file name to the class name to avoid compile errors.

2

Use Awake for internal setup

Awake to cache component refs (_rb = GetComponent<Rigidbody>()). Avoid depending on other objects here.

3

Use Start for cross-object setup

Start when you need other objects (their Awake has already finished).

4

Split Update vs FixedUpdate

Input and AI → Update. Physics forces (AddForce, velocity) → FixedUpdate to avoid glitches.

5

Clean up in OnDestroy

Unsubscribe events, stop Coroutines/timers in OnDestroy to prevent leaks.

Interactive simulator

Press Play Scene to watch lifecycle methods fire in order. Try Disable or Destroy for the next steps.

Lifecycle Timeline

Console

Press Play to begin...

Frame: |State: Inactive|Waiting for Play...

Code example

Basic

Standard MonoBehaviour layout with lifecycle methods and why each one exists.

using UnityEngine;

public class PlayerController : MonoBehaviour
{
  [SerializeField] private float speed = 5f;
  private Rigidbody _rb;
  private Vector3   _inputDir;

  void Awake()
  {
      // Cache component — no dependency on other scripts
      _rb = GetComponent<Rigidbody>();
  }

  void Start()
  {
      // Setup that needs GameManager.Awake to have finished
      GameManager.Instance.RegisterPlayer(this);
  }

  void Update()
  {
      // Read input every frame
      float h = Input.GetAxisRaw("Horizontal");
      float v = Input.GetAxisRaw("Vertical");
      _inputDir = new Vector3(h, 0, v).normalized;
  }

  void FixedUpdate()
  {
      // Physics move — do not use Time.deltaTime here
      _rb.MovePosition(_rb.position + _inputDir * speed * Time.fixedDeltaTime);
  }

  void OnDestroy()
  {
      GameManager.Instance.UnregisterPlayer(this);
  }
}

Code example

Advanced

Optimize: avoid empty Update; prefer OnEnable/OnDisable for event subscriptions.

using UnityEngine;
using System;

// Full lifecycle component — pairs well with an event system
public class HealthComponent : MonoBehaviour
{
  [SerializeField] private int maxHp = 100;
  private int _currentHp;

  public event Action<int,int> OnHealthChanged;  // (current, max)
  public event Action           OnDied;

  void Awake() => _currentHp = maxHp;

  void OnEnable()
  {
      // Subscribe when the component becomes active
      GameEvents.OnDamageDealt += TakeDamage;
  }

  void OnDisable()
  {
      // Unsubscribe immediately — avoid calls while disabled
      GameEvents.OnDamageDealt -= TakeDamage;
  }

  public void TakeDamage(int amount)
  {
      _currentHp = Mathf.Max(0, _currentHp - amount);
      OnHealthChanged?.Invoke(_currentHp, maxHp);
      if (_currentHp == 0) OnDied?.Invoke();
  }
}

📌 Quick recap

  • Awake = internal, Start = cross-object, Update = every frame
  • Physics → FixedUpdate, not Update
  • Empty Update() still costs — delete it if unused
  • OnEnable/OnDisable for subscribe/unsubscribe

⚠️ Common mistakes

  • ❌ GetComponent inside Update()

    Slow — searches every frame

    ✅ Cache in Awake: _rb = GetComponent<Rigidbody>()

  • ❌ Using a constructor: new PlayerCtrl()

    MonoBehaviour forbids new — Unity warns

    ✅ go.AddComponent<PlayerCtrl>()