I have some thoughts on Entity Component Systems design for this program, and perhaps in general.
Bruce Onder linked me to an ECS example that he liked, and I browsed some of the code. What I really wanted was a different kind of writeup than I’ve found so far, but the code was enough to give me what I think is a key understanding. I could, of course, be wrong. I frequently am.
Most of the ECS things we find are frameworks, extensive library implementations of everything the authors think you might need in order to build things in ECS style. As such, their words and code provide much more capability than we need for Asteroids, and probably much more than anyone needs for anything, with the rare exception of the few people in the world who are building large-scale games or other systems where ECS might pay off. (What are such systems, anyway? What else is ECS good for? I honestly don’t know.)
Now if you look at ECS as a design approach, it turns out that two of its three parts are nearly trivial, and one is not. The Entities are at most very simple objects and are often no more than a unique identifier, like an integer. The Components are very simple data structures, amounting, roughly, to the member variables that an object would have if you were building the program with conventional object orientation. And the Systems, well, the Systems are everything else, roughly equivalent to all the code that the Component would have if it were an object instead of a dumb data structure.
In an OO design, when you have an object, you can call the code that processes the object. In ECS, it seems that the done thing is to run the System for a given kind of Component, and that code finds all the Components of its desired types, and then processes them, typically in a batch. The Navigation system finds all the things that navigate and navigates them. The Moving system finds all the things that move and moves them. The Collision system finds all the things that could collide and sorts out their collisions.
So in an ECS, it seems that the Systems are the idea that bears the most weight. The rest is just dumb data. And the ECS frameworks therefore make the System into a first-class thing, possibly an object, possibly a function or lambda, and the framework provides the ability to define the sequence of operation of the Systems, such as first Navigate, then Move, then Collide.
So that’s nice. Just one question: why would anyone ever do that? I can think of a few possible answers, based on my reading, my thinking, and my long experience writing software that worked or that didn’t work.
- In the ECS community there seems to be a belief that ECS provides better performance, mostly based on the idea of data locality. If we were to put all the Navigation data together, and all the Moving data together, and all the Colliding data together, then when we run the Navigation System, all its data would be together in memory, and “therefore” CPU caching and other optimizations would allow the System to run faster than it would if its data was spread out among objects, all over memory, and faster because it’s looping over the data rather than doing object method dispatching.
I’m not sure whether I believe that story, and I’m even less sure how important it is in the scheme of things. But the ECS community seems to believe it, and they certainly have more experience than I have with the subject, so maybe it’s true. Even if it is true, I don’t personally care: my Asteroids program isn’t a machine killer. None of my programs are.
- Parallel Development
- I haven’t seen anyone mention this, but it seems to me that ECS might make it easier for a large game development team to work. A small sub-team could work on a single System, and because the overall program just sees
addComponentand the like, and the Components are just data structures, and when you plug a System into the pipeline, there’s no special wiring to do, you just package it up however Systems are packaged, stuff it into the pipeline, and there you go.
You could probably work on your System fairly independently, either running a stripped-down system with just your stuff in it, or running any stable version of the whole program with your new code added in. It’s easy to imagine that ECS would make conventional integration easier with multiple teams. It might even make a more continuous integration easier, because everyone would just keep their best System version at the head of the repo, and full system builds would pick up the best stuff. Integration problems would probably be fewer.
If I’m right about this advantage, and I think I might be, it doesn’t apply here chez Ron, because we do not have a large team working on Asteroids, we just have me. While I do contain multitudes, I don’t really count as a large team.
- Better Than Inheritance
- ECS is touted as being better than inheritance, and it surely is, because most inheritance schemes only allow an object to inherit from one, because inheritance of behavior is generally frowned upon, and because inheritance is very rigid, in that changing what an object inherits from is difficult.
It is “well known” that composition is “better” than inheritance, and ECS is at its base a scheme that is focused on composition. You compose your Entities by combining the specific Components that apply to that Entity and the Systems “just work”. There’s quite a bit of hand-waving in the neighborhood of “just work”, but the advantages of composition are real.
We aren’t going to accrue much advantage in Asteroids, because we aren’t using inheritance in our objects, at least not in this version. But if we continue down this path, perhaps we’ll find an interesting comparison with our previous versions. Recall that amazing interaction scheme that Hill provided, that allowed me not to use implementation inheritance. Might an ECS approach have been easier than that, or more understandable?
Possibly. We’ll consider that when we get closer to done here.
Perhaps there are other advantages to ECS. I’m new here, so have very little direct learning about ECS systems. So far, I’m finding it interesting. It’s early days: we only have the one system even close to ECS, the Timer. We’ll probably find some more as we go forward, but I think the limiting factor is that Asteroids has very limited variability in its objects, and as such, there isn’t much need for lots of Components or particularly clever Systems.
Tell ya what. I’ll publish this as a note and then start another article actually coding something.
See you there, in #257!