Summary

Every Unity script that inherits from MonoBehaviour has access to a set of lifecycle methods that the engine calls automatically at specific moments. The two most important for beginners are Start, which runs once when the scene begins, and Update, which runs every frame while the scene is playing. Understanding when each method runs — and which type of logic belongs in each — is the foundation of all Unity scripting.

Key ideas

Start() — runs exactly once, when the GameObject first becomes active in the scene. Use it for:

  • Logging initial state to verify the script is attached and configured correctly
  • Caching component references (e.g. storing a Rigidbody2D in a field so Update can use it without calling GetComponent every frame)
  • Any one-time initialisation

Update() — runs every frame, as long as the scene is playing and the GameObject is active. Use it for:

  • Reading player input
  • Moving objects
  • Ticking timers
  • Anything that must respond to the passage of time

Time.deltaTime — the number of seconds that elapsed since the last frame. Multiplying any per-frame value by Time.deltaTime converts it from “per frame” to “per second”, making it frame-rate independent. A machine running at 60fps calls Update 60 times per second with a small deltaTime; a machine at 30fps calls it 30 times with a larger deltaTime. The product is the same either way.

gameObject.name — the name of the GameObject this script is attached to. Including it in Debug.Log messages is useful when multiple objects share the same script.


In practice

using UnityEngine;
 
public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;
    private float logTimer = 0f;
 
    // Runs once when the scene begins
    void Start()
    {
        Debug.Log("[" + gameObject.name + "] PlayerController ready. Speed: " + moveSpeed);
    }
 
    // Runs every frame
    void Update()
    {
        HandleMovement();
        LogPositionPeriodically();
    }
 
    private void HandleMovement()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        // Time.deltaTime makes movement speed-per-second, not speed-per-frame
        transform.Translate(h * moveSpeed * Time.deltaTime, v * moveSpeed * Time.deltaTime, 0f);
    }
 
    private void LogPositionPeriodically()
    {
        logTimer += Time.deltaTime;
        if (logTimer >= 2f)
        {
            Debug.Log("[" + gameObject.name + "] Position: " + transform.position);
            logTimer = 0f;
        }
    }
}

Keep Update() short: delegate logic to named private methods rather than writing everything inline. A long Update body is hard to read and debug. The HandleMovement / LogPositionPeriodically pattern above is the recommended style.


Other lifecycle methods

MethodWhen it runs
Awake()Before Start, even if the script is disabled. Use for self-initialisation that other scripts’ Start may depend on.
OnEnable() / OnDisable()When the GameObject is enabled or disabled. Useful for resetting state.
FixedUpdate()At a fixed time step (default 50 times/second). Use for physics calculations instead of Update.
LateUpdate()After all Update calls this frame. Use for camera follow logic.
OnDestroy()Just before the GameObject is destroyed. Use for cleanup.

Gotchas

  • Never call Start or Update yourself. Unity calls them. If you want to run initialisation code manually, extract it into a separate method and call that.
  • Variables declared inside Start or Update are local. They exist only for that call. Fields (declared at class level) persist between frames.
  • Update does not run if the GameObject is inactive or the script component is disabled.
  • Physics-based movement belongs in FixedUpdate, not Update, to avoid erratic behaviour when frame rate fluctuates.

Practice

Create a GameObject named LifecycleProbe. Add a script with:

  • one Debug.Log in Start
  • one timer in Update
  • one private method called from Update
  • one field that stores elapsed time

Success test:

  • Start logs once when Play mode begins
  • the timer logs every two seconds
  • the log includes gameObject.name
  • no local variable is expected to remember a value between frames

Extension:

  • disable the script component during Play mode and observe that Update stops running

Self-test

  1. Which method runs once: Start or Update?
  2. Why should movement usually multiply by Time.deltaTime?
  3. A variable declared inside Update resets every frame. Where should it go if it needs to remember a value?
  4. Which lifecycle method is normally better for physics movement?
  5. Why is a short Update method easier to debug?

Answers

  1. Start runs once when the script first becomes active.
  2. Time.deltaTime turns movement into units per second, so speed stays consistent across different frame rates.
  3. It should be a field, declared at class level.
  4. FixedUpdate, because Unity runs physics on a fixed timestep.
  5. A short Update points you to named helper methods, so the likely fault is easier to find.