The ECS Pattern
Episode 4 introduces the Entity Component System pattern. Instead of rigid class hierarchies, ECS uses composition—entities are IDs, components are data, and systems contain logic. It's modular, flexible, and scales.
The Problem with Traditional Game Objects
Before ECS, most engines used inheritance hierarchies. You'd have a base GameObject class, then extend it for specific types: Player extends Character extends GameObject. Sounds clean, right?
But it falls apart fast. What if your player can fly and swim? Do you inherit from FlyingObject or SwimmingObject? Multiple inheritance gets messy. You end up with rigid hierarchies that fight against your design instead of supporting it.
That's where ECS comes in.
Entities, Components, Systems
The Entity Component System pattern breaks game objects into three pieces:
Entities are just IDs. Nothing more. They don't do anything. They're containers.
Components are pure data. A Transform component holds position and rotation. A Velocity component holds speed and direction. No methods. No logic. Just data.
Systems contain the logic. A MovementSystem queries all entities that have both Transform and Velocity components, then updates their positions. A RenderSystem queries entities with Transform and Shape, then draws them.
This separation is powerful. You compose entities by adding components. Want a moving rectangle? Add Transform, Velocity, and Shape components. Want it to stop moving? Remove Velocity. No inheritance. No rigid structure. Just data.
Building the ECS Package
Episode 4 introduces @ges/ecs, the package that implements this pattern.
The core is the World class. It manages entities, tracks which components belong to which entities, and runs systems every frame.
Here's how it works:
- Call
world.createEntity()to get a new entity ID - Call
world.addComponent(entityId, component)to attach data - Register systems that query and operate on entities with specific components
- Call
world.update(delta)every frame to run all systems
Systems Run the Show
Systems are where the game logic lives. Each system declares what components it needs, then the World provides matching entities.
In Episode 4, we built two systems:
MovementSystem — queries entities with Transform and Velocity, updates positions based on velocity and delta time.
RenderSystem — queries entities with Transform and Shape, calls the renderer to draw them at the correct positions.
Systems don't care about entity types. They care about data. If an entity has the components a system needs, the system processes it. That's it.
Integration with the Engine Loop
The engine loop now calls world.update(delta) during its update phase and world.render() during its render phase. The ECS sits right in the middle of the game loop, processing entities every frame.
This keeps everything modular. The engine doesn't know what systems exist. It just knows to call the World, and the World handles the rest.
Why This Matters
ECS scales. As your game grows, you don't refactor class hierarchies. You just add new components and systems. Need physics? Add a RigidBody component and a PhysicsSystem. Need AI? Add a Brain component and a BehaviorSystem.
Components are data. Systems are logic. Entities are IDs. That's the entire pattern. Simple, flexible, and battle-tested by engines like Unity and Unreal.
What's Next
Episode 5 expands the component library and builds out more systems. We'll add input handling, better rendering, and start making the editor feel like an actual tool instead of a tech demo.