In the ongoing, epic comments (300+ and counting!) for my Entity Systems posts, one of the recurring questions is:
What makes a good Component?
How should I split my conceptual model into Entities and Components?
How should I split my algorithms and methods into Systems?
It’s often difficult to answer these questions without concrete examples, which are thin on the ground.
Good news, then…
Paul Gestwicki runs a CS315: Game Programming course, and last year his students used an Entity System to implement their game – Morgan’s Raid. In a recent email conversation, he mentioned he’d been monitoring the actual number – and nature – of the Components and Systems that the teams developed and used on the project.
He’s now posted this list, along with some brief analysis.
All systems and Components
Read Paul’s post – there are some caveats he mentions, and there’s a useful diagram showing roughly how many systems were using each component.
I strongly recommend you play the game too (it’s free, and quick to play) so you can get an idea straight away – just from the names – what data and code some of these contain.
To recap, here’s the list:
“For your reading convenience, here’s a simple tabular view of the systems and components
Systems Components BackgroundTileSystem
Things I noticed straight away:
- There’s approximately 2:1 ratio of “components” to “systems”
- In Paul’s post, all the Systems are accessing *something*
- In Paul’s post, quite a few Components are NOT accessed
- A couple of components are used by almost every System
- The names of some Systems suggest they’re very trivial – perhaps only a dozen lines of effective code
- The names of some Components suggest they’re being designed in an OOP hierarchy
NB: I haven’t had time to look at the source code, but it’s freely downloadable here, so I’d recommend having a look if you have time.
How many Components per System?
I’ve generally started people off with: “aim for 1:1 ratio”. This is mainly to kick them out of the traditional class-based OOP mindset. In practice, there’s really no need to stick to that religiously – once you get the hang of ES design, you should be freely adding and subtracting components all over the place.
In reality, the pressures on “number of systems” and “number of components” are independent. Ideally, you add a new system when you have a major new concept to add to your game – e.g. “previously I was using hand-made jumping, now I want to add a complete physics-driven approach. This will mean changing collision-detection, changing the core game-loop, etc”.
Ideally, you add a new component when you have a new “dimension” to the game objects. For instance, if you’re adding a physics System, you may not need to add any new Components – it might be that all you need is Location (containing x,y,x position and dx,dy,dz velocity) and RenderState (containing screen-pixels x,y) – and that you already have those components.
Zero systems per component
One of the advantages of an ES is that old code can just fall off the radar and disappear. So I’m not surprised at all to see some components that appear to be unused – and it’s MUCH easier to simply delete this code from your project than it would be on a traditional OOP project. Does anything reference that data? If so, it’s a set of particular systems. For each system, you can look at MERELY the system and the component, and make a very quick decision about whether you still need this access – or if you can refactor to move (some of) it somewhere else. The amount of code you need to read to make such decisions safely is typically very small – i.e. easy, quick, and less error-prone.
Many systems per component
This is fine. However, it can also be an early-warning sign of a design or code-architecture bug. Sometimes, there are components that – innately – are just needed all over the place. For instance, in a team-based game, the component saying which “team” a given object/player/item/building belongs to is likely to affect almost every piece of algorithm code across the board. It’ll be referenced by many systems.
On the flip-side, it may be a sign that you’ve put too much data into one component. There are two usual versions of this:
- You have – say – 8 variables in the struct where you should instead have two structs (components), one with 5 variables, the other with 3.
- You have – say – 4 variables in the struct, but different systems are using those variables to mean different things. It works OK for now, but it’s very fragile – as soon as the different meanings diverge even a little, your code is going to start breaking
Of course, you get this exact problem in traditional OOP setups, but with an ES it’s trivial to fix. Split – or duplicate – the Component, change a few references in the Systems, and you’re done. If it turns out a week later that the split wasn’t necessary – or worse, was a step backwards (e.g. you find yourself frequently synching the data between those components) – it’s extremely cheap to swap it back.
By contrast, with OOP, this is a nightmare scenario, because you have to worry about every method on the original class. Does that method:
- Need to exist on both the new classes, or just one?
- Work correctly for the new class it will be on – or does it currently rely on some of the data (and shoudln’t) and will need to be re-written?
- Get used by other parts of the codebase in ways that will break if/when you split the class?
…this is just a lightning quick analysis, but I strongly invite you to do you own digging into the classes – and the codebase – and come up with your own thoughts and feedback. We have here a convenient, real-life, list of components/systems – something to dig our teeth into, and debate the rights and wrongs of each decision. And I’m sure the students involved on the project would be interested in your feedback on their approaches :)
Did this post help you?
Support me on Patreon, writing about Entity Systems and sharing tech demos and code examples