--- description: bitECS 0.4 component and system conventions globs: src/ecs/**/*.ts alwaysApply: false --- # ECS Patterns (bitECS 0.4) ## Components — Plain Objects with Arrays ```typescript // ✅ GOOD: plain object with number arrays export const Position = { x: [] as number[], y: [] as number[] }; export const Health = { current: [] as number[], max: [] as number[] }; // ❌ BAD: old API (defineComponent, Types) — removed in 0.4 ``` ## Entity Lifecycle ```typescript import { createWorld, addEntity, addComponent, removeEntity } from 'bitecs'; const world = createWorld(); const eid = addEntity(world); addComponent(world, eid, Position); Position.x[eid] = 100; Position.y[eid] = 200; ``` ## Queries — Direct Function Calls ```typescript import { query, Not } from 'bitecs'; // ✅ 0.4 API: query(world, components) const entities = query(world, [Position, Velocity]); for (const eid of entities) { Position.x[eid] += Velocity.x[eid] * delta; } // Operators: Not, And, Or, Any, None const stationary = query(world, [Position, Not(Velocity)]); ``` ## Observers (replace enterQuery/exitQuery) ```typescript import { observe, onAdd, onRemove } from 'bitecs'; observe(world, onAdd(Position), (eid) => { /* entity gained Position */ }); observe(world, onRemove(Position), (eid) => { /* entity lost Position */ }); ``` ## Systems — Pure Functions ```typescript export const movementSystem = (world: World, delta: number) => { for (const eid of query(world, [Position, Velocity])) { Position.x[eid] += Velocity.x[eid] * delta; Position.y[eid] += Velocity.y[eid] * delta; } }; ``` ## Rules - Components = plain objects with `[] as number[]` fields - Never store strings in components — use numeric IDs, look up in registry - Systems must not create Phaser objects — only the sync bridge does that - Parameter order: `addComponent(world, eid, component)` (world first, then eid)