Unity Fundamentals
MonoBehaviour Lifecycle
Every gameplay script in Unity derives from MonoBehaviour. The engine calls
a fixed sequence of messages; understanding the order is the difference
between code that runs smoothly and code that stutters or races.
Awake() -> OnEnable() -> Start()
|
v
FixedUpdate() (physics, fixed timestep) --+
|
Update() (input, gameplay) --+
|
LateUpdate() (camera follow, post-sim) --+
|
OnDisable() -> OnDestroy()
| Message | When it runs | Typical use |
|---|---|---|
Awake |
Once, when the script instance is loaded | Cache components, set up singletons |
OnEnable |
Every time the component is enabled | Subscribe to events |
Start |
Once, before the first Update | Cross-object references (other objects' Awake have run) |
FixedUpdate |
Fixed timestep (default 0.02 s = 50 Hz) | Rigidbody physics forces |
Update |
Every rendered frame | Input polling, gameplay logic |
LateUpdate |
After all Update calls have finished | Camera follow, post-processing |
OnDisable |
When the component is disabled | Unsubscribe from events |
OnDestroy |
When the GameObject is destroyed | Cleanup native resources |
Gotcha:
Camera.mainsearches the scene by tag every call in 2022.3. Cache it inAwakeif you call it more than once per frame.
GameObjects and Components
A GameObject is an empty container. Behaviour is added through
components:
var go = new GameObject("Enemy");
go.transform.position = Vector3.zero;
go.AddComponent<Rigidbody>();
go.AddComponent<CapsuleCollider>();
var ai = go.AddComponent<NPCController>();
ai.player = GameObject.FindGameObjectWithTag("Player").transform;
- Every GameObject always has a
Transform(orRectTransformunder a Canvas). You cannot remove it. - Use
GetComponent<T>()/GetComponentInChildren<T>()/GetComponentInParent<T>()to reach related components. Cache the result inAwakeinstead of calling it every frame. RequireComponent(typeof(T))makes Unity auto-add the required component when the script is added in the inspector.
The Component pattern in practice
[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
private Rigidbody _rb;
private void Awake()
{
_rb = GetComponent<Rigidbody>(); // safe: RequireComponent guarantees it
}
}
Coroutines
Coroutines let you write asynchronous-looking code that runs across multiple frames:
private IEnumerator RespawnAfterDelay(float seconds)
{
yield return new WaitForSeconds(seconds);
transform.position = _spawnPoint;
}
private void OnDeath() => StartCoroutine(RespawnAfterDelay(2f));
yield return null;waits one frame.yield return new WaitForFixedUpdate();waits until the next physics step.yield return new WaitUntil(() => _isGrounded);waits for a condition.
Inspector best practices
- Use
[Header],[Tooltip],[Range],[SerializeField]to keep the inspector tidy. - Prefer
private+[SerializeField]overpublicfor fields that should not be touched by other scripts but should be editable in the inspector. - Use
[System.Serializable]on structs/classes to make them expandable in the inspector.