# Game Programming Patterns (Review)
**Book:** *Game Programming Patterns* by Robert Nystrom, free online at <https://gameprogrammingpatterns.com/contents.html>
**Target:** Unity 6000.5 and above. Companion to [[Architecture and Code Patterns]] and [[ScriptableObject Architecture]].
## Overall review
**Structure.** 20 chapters in five groups: *Design Patterns Revisited* (Command, Flyweight, Observer, Prototype, Singleton, State), *Sequencing* (Double Buffer, Game Loop, Update Method), *Behavioral* (Bytecode, Subclass Sandbox, Type Object), *Decoupling* (Component, Event Queue, Service Locator) and *Optimization* (Data Locality, Dirty Flag, Object Pool, Spatial Partition), plus an intro chapter, "Architecture, Performance, and Games".
**Strengths**
- Each pattern opens with a one-line *Intent*, then a game-specific motivating problem, then trade-offs ("Keep in mind", "Design decisions"). That trade-off focus is the best part: the book tells you when *not* to use a pattern (the Singleton chapter is essentially an argument against it).
- Written from shipping-game experience, short, witty and free.
- The intro chapter's framing (decoupling buys flexibility but costs performance and indirection) is the right lens for every Unity architecture decision.
**What's dated or different for Unity**
- Examples are C++ with manual memory management. In C#, garbage collection replaces many of the allocation worries; your concern becomes *GC allocations per frame* instead.
- The book shows how to *build* an engine. Unity already implements several patterns (Game Loop, Update Method, Component, Double Buffer, Prototype, parts of Spatial Partition). For those, read the chapter to understand what Unity does for you and its costs, not to re-implement it.
- Data Locality predates Unity's DOTS (Entities, Burst, Jobs), which is that chapter turned into a product.
**How to read it as a Unity dev:** read the intro, then Command, Observer, State, Singleton, Service Locator, Type Object, Component, Event Queue, Object Pool. Skim Bytecode and Double Buffer.
## Pattern-by-pattern
### Design Patterns Revisited
#### Command — [chapter](https://gameprogrammingpatterns.com/command.html)
**Book:** quotes the GoF intent ("Encapsulate a request as an object... support undoable operations") and reframes it: "Commands are an object-oriented replacement for callbacks." Uses input remapping, AI driving actors through the same commands, and undo/redo.
**Unity:** the Input System's actions map naturally onto command objects; undo stacks in turn-based games and editor tools. Editor-side, `Undo.RecordObject` is Unity's own command/undo system.
**In your notes:** [[Architecture and Code Patterns]] Step 5 (command with undo) and Step 7; [[Custom Inspectors and Editor Tooling]] (Undo in the waypoint tool).
```csharp
public interface ICommand { void Execute(); void Undo(); }
public sealed class MoveCommand : ICommand {
readonly Transform t; readonly Vector3 delta;
public MoveCommand(Transform t, Vector3 delta) { this.t = t; this.delta = delta; }
public void Execute() => t.position += delta;
public void Undo() => t.position -= delta;
}
```
#### Flyweight — [chapter](https://gameprogrammingpatterns.com/flyweight.html)
**Book:** split an object into shared *intrinsic* state and per-instance *extrinsic* state, so thousands of instances (a forest of trees, terrain tiles) share one copy of the heavy data. Mentions GPU instanced rendering as the hardware form of it.
**Unity:** `sharedMesh` / `sharedMaterial` (using `.material` silently clones), GPU instancing and the SRP Batcher, Tilemap tiles (one `TileBase` asset referenced by many cells), and ScriptableObjects as shared config.
**In your notes:** [[ScriptableObject Architecture]], [[Tilemaps and Tilesets]].
#### Observer — [chapter](https://gameprogrammingpatterns.com/observer.html)
**Book:** a subject notifies observers without knowing who they are (the example is an achievements system hooked into physics code). Discusses cost, the "lapsed listener" problem (forgetting to unregister), and notes modern languages build this in.
**Unity:** C# `event`/`Action`, `UnityEvent`, ScriptableObject event channels. Always unsubscribe in `OnDisable`.
**In your notes:** [[Architecture and Code Patterns]] Step 3; [[ScriptableObject Architecture]] (event channels); [[UI Toolkit and Diegetic UI]] (UI reacting to data / data binding).
#### Prototype — [chapter](https://gameprogrammingpatterns.com/prototype.html)
**Book:** create objects by cloning a prototype instance (spawners cloning monsters), then argues the bigger win is *data modeling*: prototypes/inheritance in data files.
**Unity:** prefabs plus `Object.Instantiate` *are* this pattern; prefab variants are the data-modeling prototype chain.
**In your notes:** [[Multiplayer with Netcode for GameObjects]] (network prefabs spawned at runtime).
#### Singleton — [chapter](https://gameprogrammingpatterns.com/singleton.html)
**Book:** GoF intent "Ensure a class has one instance, and provide a global point of access to it", then mostly argues against it: it is a global variable, couples code, and hurts concurrency. Suggests passing objects in, getting them from a base class, or using a Service Locator instead.
**Unity:** the `static Instance` MonoBehaviour is everywhere. Prefer a bootstrap scene that owns services, or a service locator / DI. Watch out for Enter Play Mode Options with domain reload disabled: statics survive between play sessions, so reset them with `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]`.
**In your notes:** [[Architecture and Code Patterns]] Step 2; [[SceneManagement in Unity 6]] (persistent bootstrap scene instead of scattered singletons).
#### State — [chapter](https://gameprogrammingpatterns.com/state.html)
**Book:** GoF intent "Allow an object to alter its behavior when its internal state changes." Builds from flag soup to enum + switch, to state objects with enter/exit, then concurrent state machines, hierarchical states and pushdown automata.
**Unity:** Animator state machines (with `StateMachineBehaviour`) for animation; plain C# state classes for gameplay logic.
**In your notes:** [[Architecture and Code Patterns]] Step 4 and Step 7.
### Sequencing Patterns
#### Double Buffer — [chapter](https://gameprogrammingpatterns.com/double-buffer.html)
**Intent:** "Cause a series of sequential operations to appear instantaneous or simultaneous."
**Unity:** the swap chain/framebuffer is already double-buffered; you use it yourself with ping-ponged `RenderTexture`s (blur, fluid, Game of Life compute shaders) and with "current/next" arrays in cellular simulations.
**In your notes:** [[URP Shaders and Shader Graph]] (fullscreen effect via Render Graph, which reads one texture and writes another).
#### Game Loop — [chapter](https://gameprogrammingpatterns.com/game-loop.html)
**Intent:** "Decouple the progression of game time from user input and processor speed." Covers variable vs fixed timestep and the "fixed update, variable render" approach.
**Unity:** the engine owns the loop (the `PlayerLoop`). `FixedUpdate` + `Time.fixedDeltaTime` is the book's fixed timestep with catch-up; `Update` + `Time.deltaTime` is the variable step; Rigidbody interpolation is the book's render interpolation. You can insert custom systems with `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop`.
#### Update Method — [chapter](https://gameprogrammingpatterns.com/update-method.html)
**Intent:** "Simulate a collection of independent objects by telling each to process one frame of behavior at a time."
**Unity:** `MonoBehaviour.Update`/`LateUpdate`/`FixedUpdate`. The book's warnings apply: order between objects is undefined (use Script Execution Order or a single manager loop), and thousands of `Update` calls have overhead; a manager iterating a list is cheaper.
```csharp
public class Ticker : MonoBehaviour {
static readonly List<ITickable> items = new();
public static void Add(ITickable t) => items.Add(t);
public static void Remove(ITickable t) => items.Remove(t);
void Update() { float dt = Time.deltaTime; for (int i = 0; i < items.Count; i++) items[i].Tick(dt); }
}
public interface ITickable { void Tick(float dt); }
```
**In your notes:** [[Timeline Basics]] (Timeline drives objects per frame through playables instead of each one updating itself).
### Behavioral Patterns
#### Bytecode — [chapter](https://gameprogrammingpatterns.com/bytecode.html)
**Intent:** "Give behavior the flexibility of data by encoding it as instructions for a virtual machine."
**Unity:** rarely hand-built. The equivalents are Visual Scripting graphs, node-based dialogue/ability graphs saved as data, Lua/Ink/Yarn integrations, and Shader Graph (a graph compiled to code). Build your own only for modding or designer-authored logic.
#### Subclass Sandbox — [chapter](https://gameprogrammingpatterns.com/subclass-sandbox.html)
**Intent:** "Define behavior in a subclass using a set of operations provided by its base class."
**Unity:** an abstract `Ability : ScriptableObject` base with protected helpers (`PlaySound`, `SpawnFx`, `DealDamage`) and one abstract `Activate()`; subclasses only call the helpers. `MonoBehaviour` itself is a sandbox (`GetComponent`, `StartCoroutine`...).
**In your notes:** [[ScriptableObject Architecture]].
#### Type Object — [chapter](https://gameprogrammingpatterns.com/type-object.html)
**Intent:** "Allow the flexible creation of new 'classes' by creating a single class, each instance of which represents a different type of object." (Monster + Breed example.)
**Unity:** ScriptableObjects are the canonical Type Object: one `EnemyType` asset per breed, referenced by many enemy instances.
```csharp
[CreateAssetMenu(menuName = "Game/Enemy Type")]
public class EnemyType : ScriptableObject { public int maxHealth = 10; public string attackText = "bites"; }
public class Enemy : MonoBehaviour {
[SerializeField] EnemyType type; int health;
void Awake() => health = type.maxHealth;
}
```
**In your notes:** [[ScriptableObject Architecture]]; [[Tilemaps and Tilesets]] (custom tile assets as types); [[Architecture and Code Patterns]] Step 7.
### Decoupling Patterns
#### Component — [chapter](https://gameprogrammingpatterns.com/component.html)
**Intent:** "Allow a single entity to span multiple domains without coupling the domains to each other." Discusses how components communicate (shared state on the container, direct references, messaging).
**Unity:** the GameObject/Component model *is* this chapter. The three communication options map to shared components, `GetComponent`/serialized references, and `SendMessage`/events (prefer the latter two).
**In your notes:** applies to every note; see [[Multiplayer with Netcode for GameObjects]] for `NetworkBehaviour` components layered on objects.
#### Event Queue — [chapter](https://gameprogrammingpatterns.com/event-queue.html)
**Intent:** "Decouple when a message or event is sent from when it is processed." Example: audio requests queued in a ring buffer, merged, processed later.
**Unity:** Observer events fire immediately; a queue defers them (e.g. to process at a safe point in `LateUpdate`, or to throttle audio). Netcode RPCs are queued messages over the network.
```csharp
public class EventQueue<T> {
readonly Queue<T> q = new();
public void Post(T e) => q.Enqueue(e);
public void Drain(Action<T> handle) { while (q.Count > 0) handle(q.Dequeue()); }
}
```
**In your notes:** [[Architecture and Code Patterns]] Step 3; [[Multiplayer with Netcode for GameObjects]] (RPCs).
#### Service Locator — [chapter](https://gameprogrammingpatterns.com/service-locator.html)
**Intent:** "Provide a global point of access to a service without coupling users to the concrete class that implements it." Covers null services and logging decorators.
**Unity:** a static `Services` registry populated from a bootstrap scene; a null service (silent audio) for tests. DI frameworks (VContainer, Zenject) are the alternative.
**In your notes:** [[Architecture and Code Patterns]] Step 2; [[SceneManagement in Unity 6]] (bootstrap scene registering services).
### Optimization Patterns
#### Data Locality — [chapter](https://gameprogrammingpatterns.com/data-locality.html)
**Intent:** "Accelerate memory access by arranging data to take advantage of CPU caching."
**Unity:** DOTS: Entities stores components in contiguous chunks, Burst compiles tight loops, Jobs spread them across cores. Without DOTS: arrays of structs, `NativeArray<T>`, avoid scattered class references in hot loops.
#### Dirty Flag — [chapter](https://gameprogrammingpatterns.com/dirty-flag.html)
**Intent:** "Avoid unnecessary work by deferring it until the result is needed." Example: world transforms in a scene graph.
**Unity:** `Transform` already does this internally (`transform.hasChanged` exposes it). Use it for UI rebuilds, derived stats, and editor tools; `EditorUtility.SetDirty` is the editor's version.
```csharp
int baseDamage; int bonus; bool dirty = true; int cached;
public int Damage { get { if (dirty) { cached = baseDamage + bonus; dirty = false; } return cached; } }
public void AddBonus(int b) { bonus += b; dirty = true; }
```
**In your notes:** [[Custom Inspectors and Editor Tooling]] (`SetDirty` / serialized changes); [[UI Toolkit and Diegetic UI]] (update UI only when data changes).
#### Object Pool — [chapter](https://gameprogrammingpatterns.com/object-pool.html)
**Intent:** "Improve performance and memory use by reusing objects from a fixed pool instead of allocating and freeing them individually."
**Unity:** built in since 2021: `UnityEngine.Pool.ObjectPool<T>` (plus `ListPool<T>`, `DictionaryPool`). In C# the win is avoiding `Instantiate`/`Destroy` cost and GC spikes.
```csharp
using UnityEngine.Pool;
public class BulletSpawner : MonoBehaviour {
[SerializeField] Bullet prefab; ObjectPool<Bullet> pool;
void Awake() => pool = new ObjectPool<Bullet>(
() => { var b = Instantiate(prefab); b.Pool = pool; return b; },
b => b.gameObject.SetActive(true),
b => b.gameObject.SetActive(false),
b => Destroy(b.gameObject), collectionCheck: false, defaultCapacity: 32, maxSize: 256);
public void Fire(Vector3 p) { var b = pool.Get(); b.transform.position = p; }
}
public class Bullet : MonoBehaviour {
public IObjectPool<Bullet> Pool;
void OnBecameInvisible() => Pool.Release(this);
}
```
#### Spatial Partition — [chapter](https://gameprogrammingpatterns.com/spatial-partition.html)
**Intent:** "Efficiently locate objects by storing them in a data structure organized by their positions." Covers grids, quadtrees, BSPs, k-d trees.
**Unity:** the physics broadphase handles this for colliders (`Physics.OverlapSphereNonAlloc`, `Physics2D.OverlapCircle`); `Grid`/`Tilemap` give you a fixed grid; NavMesh is spatially indexed. Hand-roll a grid only for non-physics queries over many objects.
**In your notes:** [[Tilemaps and Tilesets]] (Grid cell lookups with `WorldToCell`).
## Related
- [[Architecture and Code Patterns]]
- [[ScriptableObject Architecture]]
- [[SceneManagement in Unity 6]]
- [[Unity Tutorials]]