Overview
Understanding when and in what order scripts (C# code) execute is crucial for Unity game development. This sequence is called the "lifecycle." Properly understanding the lifecycle enables you to implement object initialization and update logic as intended, resulting in more efficient development with fewer bugs.
This article focuses on the most important event functions in Unity's lifecycle—Awake, Start, Update, FixedUpdate, LateUpdate—explaining their roles, execution order, and effective usage.
Lifecycle Events Overview
Unity scripts have "event functions" that are automatically called when specific events occur. These functions execute in a defined order from when an object is loaded into a scene until it's destroyed. The main event function execution order is:
-
Initialization
Awake(): Called once immediately after the object is loaded. Executes before any other function.OnEnable(): Called the moment the object becomes active.Start(): Called once just before the first frame'sUpdate. Runs afterAwake.
-
Physics
FixedUpdate(): Called at fixed time intervals. Suitable for physics calculations.
-
Game Logic
Update(): Called every frame. Write main game logic and input handling here.LateUpdate(): Called every frame after allUpdatefunctions complete. Used for camera follow, etc.
-
Rendering
OnRenderObject()etc.: Processing related to object rendering.
-
Teardown
OnDisable(): Called the moment the object becomes inactive.OnDestroy(): Called just before the object is destroyed.
Key Event Functions in Detail
Initialization: Awake() vs Start()
Both Awake() and Start() are used for initialization, but have important timing differences.
| Function | Execution Timing | Primary Use |
|---|---|---|
Awake() | Immediately after script instance loads. Always before Start(). | Getting own component references; initialization not dependent on other objects. |
Start() | Just before first frame's Update(). After Awake(). | Initialization that assumes other objects' Awake() has completed. |
Crucially, all objects' Awake() completes before any object's Start() is called. Therefore, when passing references between scripts, secure references in Awake() and use them for processing in Start().
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
private GameManager gameManager;
// Get own components in Awake
void Awake()
{
rb = GetComponent<Rigidbody>();
Debug.Log("Awake: Got Rigidbody.");
}
// Processing that assumes other objects are initialized goes in Start
void Start()
{
gameManager = FindObjectOfType<GameManager>();
gameManager.RegisterPlayer(this);
Debug.Log("Start: Registered player with GameManager.");
}
}
Updates: Update() vs FixedUpdate() vs LateUpdate()
These functions are called every frame or at similar frequency, but have distinct purposes.
| Function | Execution Timing | Primary Use |
|---|---|---|
FixedUpdate() | Fixed time intervals (default 0.02 seconds). Frame rate independent. | Physics using Rigidbody (applying forces, etc.). |
Update() | Every frame. Interval varies with frame rate. | Input handling, time-based movement, general game logic. |
LateUpdate() | After all Update() completes. | Camera follow, character IK (Inverse Kinematics), etc. |
FixedUpdate() synchronizes with the physics engine update timing, so physics operations like applying forces to Rigidbody must happen here. Update() is called per-frame, suitable for input detection and visual updates not directly related to physics. Multiply by Time.deltaTime for frame rate-independent movement.
LateUpdate() is for processing that should run after all other objects' Update() movement completes. For example, after a player character moves in Update(), the camera follows in LateUpdate(). This prevents camera movement from stuttering.
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
public Transform target;
public Vector3 offset;
// Update camera position after target movement completes
void LateUpdate()
{
if (target != null)
{
transform.position = target.position + offset;
}
}
}
Performance Considerations
Update() and FixedUpdate() are called very frequently, so heavy processing in these functions seriously impacts overall game performance. Points frequently raised by experienced developers:
- Avoid overusing
Update(): Separate processing that doesn't need to run every frame. For example, use event-driven design or coroutines for conditional processing. - Cache
GetComponent: CallingGetComponent<T>()insideUpdate()is extremely inefficient. Retrieve needed components once inAwake()orStart()and store (cache) them in variables. - Custom Update Managers: When hundreds or thousands of objects have
Update(), the overhead of calls between Unity's native code and C# can become problematic. Creating a singleton class (custom update manager) that manages update processing and calls each object's update method can improve performance.
Summary
Understanding Unity's lifecycle is essential knowledge for all Unity developers, from beginners to professionals. Knowing when and why each event function is called enables you to write more reliable, performant code.
- Initialization: Prepare yourself in
Awake(), begin coordination with other objects inStart(). - Updates: Physics in
FixedUpdate(), input/logic inUpdate(), follow processing inLateUpdate(). - Performance: Avoid heavy processing in
Update(), cache components consistently.
Master these fundamentals and take your first step toward efficient Unity development.