Skip to content

Patterns from Game Engines

Game engines push patterns to their limits — every frame counts at 60fps.

PatternProjectWhereWhat It Does
Object PoolGodotcore/templates/pooled_list.hFreelist-based pool for entities, particles, physics bodies
Double BufferingSDLsrc/render/SDL_render.cFront/back buffer swap for tear-free rendering
Free ListGodotcore/templates/pooled_list.hNon-intrusive freelist allocator for O(1) entity alloc/free
Ring BufferGame audioVarious enginesLock-free audio streaming buffers between main and audio threads
State MachineGodotscene/animation/animation_tree.hAnimation state machines for character animation blending
Arena AllocatorFrame allocatorsCommon patternPer-frame bump allocator — reset every frame, zero free cost
FlyweightGodotservers/rendering/Shared mesh/texture resources referenced by multiple instances
Batch ProcessingGodot / UnityRender batchingBatch draw calls to minimize GPU state changes
Tagged UnionGodotvariant.hVariant::Type enum + union — every GDScript value is a Variant
Dirty FlagGodot / UnityTransform hierarchiesDirty flag on parent transform invalidates child world matrices — recompute only when accessed
Event LoopGodotmain_loop.hMain game loop — process input, update, render in fixed-step cycle

How They Compose: One Game Frame

At 60fps, each frame has ~16ms. Multiple patterns work together inside that budget:

Frame N starts
1
Event Loop

The main loop ticks: process input, run physics, update game state, render. Fixed timestep ensures deterministic simulation.

2
Object Pool + Free List

Spawning a bullet grabs a slot from the pool (O(1)), not malloc. Destroyed entities return to the free list.

3
Dirty Flag

Moving a parent transform marks children dirty. Only recompute world matrices for nodes that actually changed this frame.

4
State Machine

Character animation transitions (idle → run → jump) driven by state machine. Each state knows its blend tree and exit rules.

5
Batch Processing

Draw calls are batched by material/texture to minimize GPU state changes. 100 sprites with same texture = 1 draw call.

6
Double Buffering

The back buffer is swapped to front atomically. Players see a complete frame, never a half-drawn one.

7
Arena Allocator

Per-frame temp allocations (particles, debug draws) use a bump allocator. At frame end: reset pointer to zero. Done.

The key insight: game engines minimize per-object overhead. Pools avoid malloc, dirty flags avoid recomputation, batching avoids GPU calls, and arenas avoid per-object deallocation. All of these share the same design philosophy — pay O(1) per operation, defer or amortize the expensive work.

Further Reading

Released under the MIT License.