Introduction
Entity–component systems are an object-oriented architectural technique.
There is no universal consensus of what the term means, same as object-oriented programming. However, it is clear that entity–component systems are specifically intended as an architectural alternative to inheritance. Inheritance hierarchies are natural for expressing what an object is, but in certain kinds of software (such as games), you would rather express what an object does.
It is a different object model than the “classes and inheritance” one to which you’re most likely accustomed from working in C++ or Java. Entities are as expressive as classes, just like prototypes as in JavaScript or Self—all of these systems can be implemented in terms of one another.
Examples
Let’s say that Player
is an entity with Position
, Velocity
, and KeyboardControlled
components, which do the obvious things.
entity Player:
Position
Velocity
KeyboardControlled
We know Position
must be affected by Velocity
, and Velocity
by KeyboardControlled
. The question is how we would like to model those effects.
Entities, Components, and Systems
Suppose that components have no references to one another; an external Physics
system traverses all Velocity
components and updates the Position
of the corresponding entity; an Input
system traverses all KeyboardControlled
components and updates the Velocity
.
Player
+--------------------+
| Position | \
| | Physics
/ | Velocity | /
Input | |
\ | KeyboardControlled |
+--------------------+
This satisfies the criteria:
The systems are now responsible for handling events and enacting the behaviour described by the components. They are also responsible for handling interactions between entities, such as collisions.
Entities and Components
However, suppose that components do have references to one another. Now the entity is simply a constructor which creates some components, binds them together, and manages their lifetimes:
class Player:
construct():
this.p = Position()
this.v = Velocity(this.p)
this.c = KeyboardControlled(this.v)
The entity might now dispatch input and update events directly to its components. Velocity
would respond to updates, and KeyboardControlled
would respond to input. This still satisfies our criteria:
Here component interactions are explicit, not imposed from outside by a system. The data describing a behaviour (what is the amount of velocity?) and the code that enacts it (what is velocity?) are coupled, but in a natural fashion. The data can be viewed as parameters to the behaviour. And some components don’t act at all—a Position
is the behaviour of being in a place.
Interactions can be handled at the level of the entity (“when a Player
collides with an Enemy
…”) or at the level of individual components (“when an entity with Life
collides with an entity with Strength
…”).
Components
What is the reason for the entity to exist? If it is merely a constructor, then we can replace it with a function returning a set of components. If we later want to query entities by their type, we can just as well have a Tag
component which lets us do just that:
function Player():
t = Tag("Player")
p = Position()
v = Velocity(p)
c = KeyboardControlled(v)
return {t, p, v, c}
Interactions must now be handled by abstract queries, completely decoupling events from entity types. There are no more entity types to query—arbitrary Tag
data is probably better used for debugging than game logic.
Conclusion
Entities are not functions, rules, actors, or dataflow combinators. They are nouns which model concrete phenomena—in other words, they are objects. It is as Wikipedia says—entity–component systems are a software architecture pattern for modeling general objects.