L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

ecs-architecture

ecs-architecture

Expert ECS (Entity Component System) architecture patterns - storage models, system scheduling, archetypes, cache-friendliness, and real-world engine comparisons.

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: ecs-architecture description: Expert ECS (Entity Component System) architecture patterns - storage models, system scheduling, archetypes, cache-friendliness, and real-world engine comparisons.

ECS Architecture (Entity Component System) — Deep Guide

ECS is a data-driven design pattern for game engines that separates entities (IDs), components (pure data), and systems (logic operating on component sets). Its core win: cache-friendly iteration over contiguous component arrays plus implicit parallelism.

1. Core Concepts

  • Entity: a unique ID (uint32 index + generation). No data, just identity.
  • Component: a plain struct; data only, no behavior.
  • System: logic that reads/writes specific component sets, executed each frame (or at defined rate).
struct Transform { Vec3 pos; Quat rot; Vec3 scale; };
struct Velocity  { Vec3 linear; Vec3 angular; };

void moveSystem(Slice<Transform> t, Slice<Velocity> v, float dt) {
    for (i) t[i].pos += v[i].linear * dt;      // two contiguous arrays
}

2. Why ECS: Performance Reasoning

Traditional OOP (GameObject with Actor::Update() virtual call per object) has:

  • Vtable indirection → breaks ILP, no inlining.
  • Objects scattered in heap → cache misses (each 64-byte cache line may hold wasted data).
  • Member layout interleaves hot/cold data.

ECS wins:

  • Components stored in contiguous arrays (SoA) → cache streaming.
  • Systems are for loops over slices → auto-vectorizable (SIMD).
  • Systems with disjoint write sets run in parallel.

3. Storage Strategies

3.1 Sparse-Set (Sparse Array)

dense[]:  component data in entity order, tightly packed
sparse[]: entity id -> index into dense
  • Add/remove O(1) with swap-remove.
  • Iteration touches only dense[] (contiguous, cache-friendly) + entity ids.
  • Used by EnTT default.

3.2 Archetype (Structure of Arrays per group)

Entities grouped by exact component signature (archetype). Each archetype owns a column array per component:

Archetype "Transform+Velocity":
  entityIDs: [12, 17, 9]
  Transform: [t12, t17, t9]   contiguous
  Velocity:  [v12, v17, v9]   contiguous
  • Adding/removing a component moves the entity to another archetype (type change).
  • Iterating a system = iterate matching archetypes only. Perfect locality.
  • Used by Unity DOTS, Flecs, Bevy (design).

3.3 Hybrid / ECS-storage-as-optional

Many engines allow per-entity "prefab-like" dynamic storage. Choose based on:

FactorSparse-setArchetype
Add/remove component costO(1)move entity (memcpy)
Iteration localitydense per componentperfect array per archetype
Fragmentationnonemany archetypes = sparse
Simplicitysimplerneeds archetype registry

4. Querying & Matching

A system declares required components: Query<Transform, Velocity>(All).

  • Archetype-based: match on bitmask of signature → iterate matching archetypes.
  • Sparse-set: iterate dense[], check presence via storage.contains(entity).

Entity IDs filtering: any/maybe-style optional components need a "present bitset" check; optional components can iterate a second storage.

5. System Scheduling

5.1 Ordering & Dependencies

Systems schedule in a DAG respecting read/write on component storages:

TransformSystem <-> PlayerMovement <-> CameraSystem
(Velocity RW)     (Transform RW)     (Transform R)
  • Build a graph of per-storage access; parallelize systems whose storages don't conflict.
  • After(system), Before(system) ordering; or "this system writes X so those that read X run next".
  • Job-based: each system dispatched to worker threads (Bevy ParForEach, Unity ScheduleParallel).

5.2 Per-Frame vs Fixed-Rate

  • Simulation systems tied to fixed timestep (30/60/120Hz).
  • Rendering interpolation reads sim state with lerp factor.

5.3 Command Buffers / Deferred Structural Changes

Adding/removing components mid-iteration invalidates iterators. Rule: queue structural changes in a command buffer / deferred list applied after iteration. (Unity EntityCommandBuffer, Bevy Commands.)

6. Cross-Cutting Concerns

6.1 Change Detection (Dirty Flags)

  • Each storage keeps a per-frame "changed" bitset or version per archetype row.
  • Systems like "network send", "recompute bounds", or "update render node" only run on entities whose components changed.
  • Bevy Added<T>, Changed<T> filters; Unity SystemAPI.GetComponentLookup.

6.2 Relationships & Query Hierarchies

Parent/child: store optional Parent + Children components. World-space recompute only on dirty leaf.

6.3 Events & Messages

ECS events (collision events, input) as transient entity/component pools — event systems read then delete. Avoid per-frame entity spam; use component-versioned event queues for perf.

7. Serialization / Networking / Replays

  • Entities serialized by id + comp list; archetype moves restored on load.
  • Network: replicate only changed components using change detection; hide float noise by bit-snapping.
  • Replays: log system inputs; deterministic ECS reproduces.

8. Real Engines Comparison

EngineStorageStrengths
Unity DOTSArchetype (Burst/Jobs)SIMD burst, editor tooling, ECS + jobs
BevyArchetype (Rust)safe parallelism, great ergonomics, GPU-driven
EnTTSparse-set (C++)no-reflection, header-only, fastest add/remove
FlecsArchetype+C (C/... plugins)fast, relations, prefab support, single include
Machina/Garnetsparse / archeniche

8.1 When NOT to use ECS

  • Tiny simple games: GameObject/MonoBehaviour faster to prototype.
  • UI, narrative systems, inventory logic with heavy dynamic mixing.
  • Deep polymorphic AI with per-entity context → still can be ECS but readability drops.
  • If the team lacks data-oriented mindset, ECS can hurt velocity.

9. ECS Anti-Patterns

Anti-patternConsequence
Components with pointers/referencesbreaks array continuity, serialization / cache
Systems calling GetComponent<T> in a looppointer chase kills the approach
Structural changes mid-iterationiterator invalidation / undefined
One giant "Stats" componenthot/cold mix, cache waste
Over-strict ECS religion (all kinds, even trivial)boilerplate bloat
Global mutable state in systemsbreaks parallel schedule / determinism

10. ECS Decision Tree

flowchart TD
    A{Per-frame hot? Many entities?} -->|Yes| B{Change component set often?}
    A -->|No| C[Classic OOP ok]
    B -->|Yes| D[Sparse-set storage]
    B -->|No| E[Archetype storage]
    E --> F{SIMD + safety via language?}
    F -->|Rust| G[Bevy]
    F -->|C#| H[Unity DOTS]
    F -->|C++| I[EnTT / Flecs]
    D --> J{Simplicity? Language?}
    J -->|C++| I

11. Deep Dive References

Deep storage internals, scheduling, and engine-specific patterns live in:

  • skills/game/game-engine/ecs-pattern/SKILL.md (full deep dive: archetypes, scheduling, SIMD, benchmarks)
  • skills/game/game-engine/ecs-pattern/references/archetype-storage-deep-dive.md
  • skills/game/game-engine/ecs-pattern/references/system-scheduling-pipelines.md
  • skills/game/game-engine/ecs-pattern/references/unity-dots-in-practice.md
  • skills/game/game-engine/ecs-pattern/references/bevy-ecs-patterns.md
  • skills/game/game-engine/ecs-pattern/references/entt-and-flecs.md

12. Getting Started Checklist

  1. Model components as structs; split hot vs cold data.
  2. Choose storage (sparse-set for churn, archetype for bulk).
  3. Define systems per frame; build dependency graph.
  4. Implement command buffer for structural changes.
  5. Add change-detection for dirty-driven systems.
  6. Measure: iteration throughput, cache misses, parallel speedup.
Skills enregistrées