File size: 3,936 Bytes
40c0886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# 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.main` searches the scene by tag every call in 2022.3.
> Cache it in `Awake` if you call it more than once per frame.

## GameObjects and Components

A `GameObject` is an empty container. Behaviour is added through
**components**:

```csharp
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` (or `RectTransform` under a
  Canvas). You cannot remove it.
* Use `GetComponent<T>()` / `GetComponentInChildren<T>()` /
  `GetComponentInParent<T>()` to reach related components. Cache the result
  in `Awake` instead 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

```csharp
[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:

```csharp
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]` over `public` for 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.