Parsec Patrol Diaries: Entity Component Systems
The Entity, Component, & System design pattern is old hat for many game developers. But, keep in mind that I’m a web developer, and mostly on the server side of things for the past decade or so. One of my last big revelations was discovering the Model, View, & Controller way of doing things. Apropos of that, this ECS thing seems to be a Big Deal of similar proportions.
When I first started working on
Parsec Patrol, I started sketching out a plain vanilla class hierarchy. You know, the kind I saw when I first started learning about Object Oriented Programming.
I started with an
Entity, which begat a
RenderableEntity, which begat things like a
SpaceShipWithThrustersRenderableEntity. I built a game loop that iterated through all the objects in the universe, calling a
tick() method on each in turn. Simple, just like things I’d seen in textbooks.
As I started trying to pile things in, though, I flirted with multiple inheritance and primitive composite patterns and everything just got messy and slow. Performance sucked, and garbage collection went wild. So, I figured I must be Doing It Wrong, and started on a Google quest into game engine design to maybe absorb some modern thinking on these matters. Eventually, I stubbed my brain on the notion of an “entity/component system”.
Plenty of other blog posts & articles out there have done a great job of describing how an ECS design works:
- Entity Systems are the future of MMOG development (Sep 2007)
- Evolve Your Hierarchy (Jan 2007)
- Why use an entity system framework for game development? (Feb 2012)
- Anatomy of a Knockout (Dec 2012)
- Understanding Component-Entity-Systems (April 2013)
- Artemis Entity System Framework
- Ash entity framework
I don’t want to totally reinvent the wheel here. But, let’s see if I can’t break it down a little, if only to convey my excitement with discovering this design pattern and the fun I’ve had with it.
First, you’ve got the Entity: Everything in the game universe is an Entity – ships, rocks, missiles, the works. In my naive OOP world, Entities were instances of classes from my tangly, thorny hierarchy.
But, in this ECS world, an Entity is a database ID – just a string, really.
Wait, what? Yeah, that was the first thing that bruised my lobes. This design pattern turns everything inside out. An ECS framework is better understood as a data-oriented system than an object-oriented system. In fact, a blog post describing an ECS system in relational database terms really drove it home for me.
So, given that an Entity is an ID in a system that looks like a SQL database from a certain angle, what’s in the database?
Components are in the database. Specifically, Component types correspond to tables and Component instances correspond to rows. Structurally, a Component is a collection of properties – not unlike the columns in a row in a table in a database.
In case it doesn’t quite make sense yet, here are some sample Components:
- Sprite – width, height, shape
- Position – x, y, rotation
- Motion – dx, dy, drotation
- Thruster – accel_per_second, max_speed
- Seeker – target_entity_id, rotation_per_second
- Health – max_health, current_health
- BeamWeapon – max_power, current_power, recharge_per_second
- MissileWeapon – number_turrets, reload_delay
Imagine each of those as schema for database tables, each with the Entity ID as primary key. Given an Entity ID, you can query across all the tables and assemble a set of Components that completely describe an Entity in the game universe.
Note that any given Entity can be composed of any combination of these Components: A spaceship might be described by Shape, Position, Motion, Thruster, Health, and BeamWeapon. Meanwhile, an asteroid drifting in space might only offer Shape, Position, and Motion.
So, where’s the stuff that makes things go? In an OOP design, this would live in the methods of the objects, bundled by classes to go alongside the data. In the ECS design, Entities are just IDs and Components are just data – neither methods nor code implementing game logic live in either of those artifacts.
This is where Systems come in: Systems are modular mini-game loops. The “master” game loop holds a list of available systems and runs a
tick() method on each of those in turn. Each System performs a query against the Component database – usually for all instances of a particular type – and crunches through updating the properties for each.
Consider a MotionSystem:
- Fetch all the Motion components from the database
- For each Motion, look up a Position for the same Entity.
- Update the
rotationproperties of the Position using the Motion properties.
Consider a SeekerSystem:
- Fetch all the Seeker components.
- For each, find the Motion and Position components for the corresponding Entity.
- Also find Motion & Position for the Entity identified as the
- Calculate the angle between seeker & target, update Motion to steer toward the target.
Notice that each system deals only with data directly relevant to the job at hand. Rather than loading up a full representation of an Entity, each System only touches the specific Components needed. For instance, MotionSystem and SeekerSystem touch Motion and Position **- but never Shape Components**.
This adds some efficiencies for data stored entirely in memory. But, I expect this will have huge implications for data that might someday come from a database or over a network. I’m also thinking that this pattern lends well to shuffling certain systems off onto background threads or Web Workers – the need for data coordination is limited to just the relevant Components when needed.
Putting it together
**These Systems feel a lot like particle systems: Tight, focused code working close to the metal with data, skipping a lot of overhead and object juggling. Components get modified in place and are rarely discarded – that seems to save me from a lot of garbage collection issues. And, if a particular Component is not actually used by any existing Entity, the System** looking for it just won’t perform any work.
For the most part, Systems act independently & are very loosely coupled. They can cooperate through shared Component data – consider how the SeekerSystem modifies the Motion Component, and the MotionSystem uses that data to move the Position Component. I’m also playing with a crude pub/sub messaging system for things like transmitting damage from one Entity to another.
And, once I was freed from my tangled class hierarchy, things started to get fun. The mental cost for adding a new layer to the system dropped fast: Add a new Component or two, add a new System – and suddenly Entities can do new things!
Having fun with it
And, the most fun part? I get to describe things in the game world like a five-year-old and not give a crap about serious programming stuff:
I’ve got a bad guy space ship! And it’s shaped like an arrow! And it’s got rocket thrusters! And it chases the good guy! And it has missile launchers! And it blows up when the hero shoots it a lot!
Oh, and I’ve got a good guy ship! And it’s shaped like a cool thing different than an arrow! And it’s also got rocket thrusters! And it goes where you click! And it has lots of laser beam guns!
Oh, and there are rocks! They kind of spin a bit and drift around! They bounce when they run into things! Sometimes they blow up!
That’s almost exactly the running dialog in my head when I write the code. It’s pretty cool.