Entity Component System architectures (and why you should care)
--
Games seem like a great application for OOP — there are lots of game objects interacting with each other and updating their state accordingly. And it’s true — many great games have been written this way.
But there are also some glaring flaws with OO design.
If you just want to know about ECS and not the problem with OOP that gave birth to ECS, you can skip to the Composition section.
The Big Problem with OOP
Inheritance
It’s one of the first concepts taught to students learning OOP, and for good reason — it’s one of the foundational pillars of OOP.
I imagine an explanation would go something like this:
Dogs and cats are both animals. They share some characteristics, such as having legs, while others differ, such as cats having whiskers but dogs not. To express this,
Dog
andCat
are both classes that inherit fromAnimal
. In this way, the shared code (such as legs) are implemented in theAnimal
class, which helps prevent code duplication. Similarly, code common to all dogs would go in theDog
class. This helps prevent code duplication between classes such saBulldog
andPoodle
which are bothDog
s.
Sounds great! We can successfully apply DRY (Don’t Repeat Yourself) in an intuitive way using inheritance trees!
The problem with inheritance
Say we have Horse
and Donkey
classes. A Mule
is an animal which has a horse mother and a donkey father. (Yes, they exist!)
Where would a mule go in the inheritance tree?
It has characteristics of both horses and donkeys, as well as its own. If it inherits from Horse
, then we have to duplicate the Donkey
code, and if it inherits from Donkey
, then the Horse
code is duplicated.
There must be a way to inherit from both, right?
Multiple inheritance to the rescue!
What if a class could inherit from multiple parent classes? This is known as multiple inheritance.
Could this be the solution?
…or not.
This is known as the Deadly Diamond of Death. It poses the question: If class D’s parents both have a method with the same name, which method is inherited by D?
There’s no clean solution to this problem so most languages don’t support multiple inheritance.
Composition
The other way to go about things in OOP is composition.
Composition is composing a class by building it out of several smaller, more specialised component classes. This allows for more flexibility and completely solves the inheritance problem. By simply picking and choosing the functionality needed, inheritance is avoided altogether.
The outer class no longer has any of its own functionality, so I’ll refer to it as the container class.
However, there’s now a new problem.
Say we have an object with Position
and Velocity
components. Where would the function updatePosition
live? A function has to be bound to one component, even though in many cases, like this one, the action it performs involves many components.
ECS
Finally, we arrive at ECS, the brilliant solution to all of the problems above.
Instead of components having methods, why not have an external function update the components instead? This would mean that the function isn’t bound to any one component, but instead is an outside force which can affect all components involved in said action.
This is the core idea of ECS.
ECS is made up of three main parts:
- Entity, which holds components.
- Component, which holds data.
- System, which performs operations on entities and components.
Systems only operate on entities that have certain components, for example, the Movement
system would only care about entities with Position
and Velocity
components. This means that there are no more dependencies, since any entity can have any combination of components and only those with a matching set will be affected by any system.
ECS is definitely a fresh way to think about many problems, and has great potential.
I’m the author of WolfECS, a new ECS framework for written in Typescript. It’s the fastest web-based ECS implementation I know of. WolfECS abstracts away a lot of the underlying implementation of ECS so all you have to worry about is your components and systems. https://github.com/EnderShadow8/wolf-ecs