Understanding Component-Entity-Systems

Published April 02, 2013 by Boreal Games, posted by Klutzershy
Do you see issues with this article? Let us know.
Advertisement
The traditional way to implement game entities was to use object-oriented programming. Each entity was an object, which intuitively allowed for an instantiation system based on classes and enabled entities to extend others through polymorphism. This led to large, rigid class hierarchies. As the number of entities grew, it became increasingly difficult to place a new entity in the hierarchy, especially if the entity needed a lot of different types of functionality. Here, you can see a simple class hierarchy. A static enemy does not fit well into the tree. classdiagram.png To solve this, game programmers started to build entities through composition instead of inheritance. An entity is simply an aggregation (technically a composition) of components. This has some major benefits over the object-oriented architecture described above:
  1. It's easy to add new, complex entities
  2. It's easy to define new entities in data
  3. It's more efficient
Here's how a few of the entities above would be implemented. Notice that the components are all pure data - no methods. This will be explained in detail below. compositiondiagram.png

The Component

A component can be likened to a C struct. It has no methods and is only capable of storing data, not acting upon it. In a typical implementation, each different component type will derive from an abstract Component class, which provides facilities for getting a component's type and containing entity at runtime. Each component describes a certain aspect of an entity and its parameters. By themselves, components are practically meaningless, but when used in conjunction with entities and systems, they become extremely powerful. Empty components are useful for tagging entities.

Examples

  • Position (x, y)
  • Velocity (x, y)
  • Physics (body)
  • Sprite (images, animations)
  • Health (value)
  • Character (name, level)
  • Player (empty)

The Entity

An entity is something that exists in your game world. Again, an entity is little more than a list of components. Because they are so simple, most implementations won't define an entity as a concrete piece of data. Instead, an entity is a unique ID, and all components that make up an entity will be tagged with that ID. The entity is an implicit aggregation of the components tagged with its ID. If you want, you can allow components to be dynamically added to and removed from entities. This allows you to "mutate" entities on the fly. For example, you could have a spell that makes its target freeze. To do this, you could simply remove the Velocity component.

Examples

  • Rock (Position, Sprite)
  • Crate (Position, Sprite, Health)
  • Sign (Position, Sprite, Text)
  • Ball (Position, Velocity, Physics, Sprite)
  • Enemy (Position, Velocity, Sprite, Character, Input, AI)
  • Player (Position, Velocity, Sprite, Character, Input, Player)

The System

Notice that I've neglected to mention any form of game logic. This is the job of the systems. A system operates on related groups of components, i.e. components that belong to the same entity. For example, the character movement system might operate on a Position, a Velocity, a Collider, and an Input. Each system will be updated once per frame in a logical order. To make a character jump, first the keyJump field of the Input data is checked. If it is true, the system will look through the contacts contained in the Collider data and check if there is one with the ground. If so, it will set the Velocity's y field to make the character jump. Because a system only operates on components if the whole group is present, components implicitly define the behaviour an entity will have. For example, an entity with a Position component but not a Velocity component will be static. Since the Movement system uses a Position and a Velocity, it won't operate on the Position contained within that entity. Adding a Velocity component will make the Movement system work on that entity, thus making the entity dynamic and affected by gravity. This behaviour can be exploited with "tag components" (explained above) to reuse components in different contexts. For example, the Input component defines generic flags for jumping, moving, and shooting. Adding an empty Player component will tag the entity for the PlayerControl system so that the Input data will be populated based on controller inputs.

Examples

  • Movement (Position, Velocity) - Adds velocity to position
  • Gravity (Velocity) - Accelerates velocity due to gravity
  • Render (Position, Sprite) - Draws sprites
  • PlayerControl (Input, Player) - Sets the player-controlled entity's input according to a controller
  • BotControl (Input, AI) - Sets a bot-controlled entity's input according to an AI agent

Conclusion

To wrap up, OOP-based entity hierarchies need to be left behind in favour of Component-Entity-Systems. Entities are your game objects, which are implicitly defined by a collection of components. These components are pure data and are operated on in functional groups by the systems. I hope I've managed to help you to understand how Component-Entity-Systems work, and to convince you that they are better than traditional OOP. If you have any questions about the article, I'd appreciate a comment or message. A follow-up article has been posted, which provides a sample C implementation and solves some design problems. Implementing Component-Entity-Systems

Article Update Log

1 April 2013 - Initial submission 2 April 2013 - Initial publication; cleaned up formatting 29 September 2013 - Added notice of follow-up article; changed some formatting)
Cancel Save
15 Likes 35 Comments

Comments

Lightness1024

Oh, I love the diagrams, it is even clearer and faster to understand that the introduction written by Richard Lord (Ash Framework). Good job yay !

April 02, 2013 02:49 PM
Alpha_ProgDes

This is concise and easy to understand. A real nice Intro to ECS!

April 02, 2013 06:04 PM
evolutional

Like this. Nice intro, look forward to seeing some sample implementation

April 02, 2013 07:38 PM
jsvcycling

Great article. What did you use to create the diagrams?

April 02, 2013 09:24 PM
tychon

I agree, this is a nice intro. Are you planning to follow this up with an article on systems and design challenges? This could easily be made into a small series, especially with how often the topic comes up. (Well, maybe not often relative to "Which language?")

April 02, 2013 09:28 PM
Klutzershy

Great article. What did you use to create the diagrams?

I used PlantUML, a text-based UML graphing tool.

I agree, this is a nice intro. Are you planning to follow this up with an article on systems and design challenges? This could easily be made into a small series, especially with how often the topic comes up. (Well, maybe not often relative to "Which language?")

I think so, yes. There's a lot more to CES than just the theory.

April 02, 2013 09:44 PM
tychon

Wonderful. Looking forward to it.

April 02, 2013 10:02 PM
Krohm

I like how you presented this thing. Many forum posts I've read on component system tend to be extremely elaborated and fail to point out what really counts. It appears to me you have been very good at condensing the core notions.

Not related to your article but to Component Systems in general, how would you deal with gameplay specific behaviours?

Here's an example for the purpose of discussion.

  • A simulated entity A={collision brush, mass, graphical model, movement} hits an object 'B'. In this case there's "typical" response, we have no problem.
  • Same as before but A has now got a power-up which allows it to ignore collisions with objects of type B. Or perhaps even worse, the specific object B.

I'm currently using overriden functions for that, it feels a bit too inheritance-based but I don't see how to improve that. I was thinking about some "filter" components but the concept seems to be quite vague. What do you think? Does it even qualifies for being called component-based?

April 03, 2013 10:08 AM
Adaline

Thank you

April 03, 2013 10:48 AM
Klutzershy

Not related to your article but to Component Systems in general, how would you deal with gameplay specific behaviours?

In at least your specific case, I would maybe have a filter component. Your physics system (or possibly a separate system) can be in charge of registering these "collision groups" with your physics engine.

Lots of gameplay features can be implemented with dynamic components.

April 03, 2013 01:07 PM
Kylotan

I do like this article, but I think it's worth pointing out that not every component-based system has to treat components as structs of data with no behaviour, moving all the behaviour into systems. Some component-based systems will encapsulate the behaviour into the component, such as with Unity's system for example. The benefit of this is that it's easier to mix and match components as they are usually self-contained. The downside is that when components do rely on each other, these dependencies are not always obvious.

April 03, 2013 04:01 PM
Zipster

I do like this article, but I think it's worth pointing out that not every component-based system has to treat components as structs of data with no behaviour, moving all the behaviour into systems. Some component-based systems will encapsulate the behaviour into the component, such as with Unity's system for example. The benefit of this is that it's easier to mix and match components as they are usually self-contained. The downside is that when components do rely on each other, these dependencies are not always obvious.

I've found, after many years of working with such a component system, that very few components tend to be entirely self-contained. It's very difficult to orthogonalize a lot of behavior in such a way that it can operate in isolation from other behavior, however the goal of a component system is to compartmentalize as much as possible. As a result, the more you try to split apart components the more they need to communicate to function, and the more you merge them together the less they are truly individual "components", rather than globs of somewhat-related data and functionality. I suppose you could say that separating the data and the behavior eases this pressure a bit, and makes it easier to split apart the data while also establishing clear dependencies between it and the behavior.

April 03, 2013 05:24 PM
CC Ricers

This article is pretty good at clearly explaining many things. If I have game Entities derive from an interface as they have in common a group of Components and a Name ID, should I also derive my Systems from a common interface?

April 04, 2013 06:20 PM
Klutzershy

This article is pretty good at clearly explaining many things. If I have game Entities derive from an interface as they have in common a group of Components and a Name ID, should I also derive my Systems from a common interface?

First of all, game entities shouldn't be deriving from an interface. Specialization is done through adding components. Systems should be derived from an interface to handle automatic component registering.

April 04, 2013 09:09 PM
Black Knight

How would systems operate on a collection of components? And what would be a good way to store components? As I undersand from the article components derive from an interface and are stored under entities. This leads to me something like :


class IComponent{};

class Position : public IComponent
{
public:
float x,y;
};

class Velocity: public IComponent
{
public:
float x,y;
};

class Entity
{
public:
std::vector<IComponent*> components;
};
 

Afterwards a Physics system operates on the components of entities to add velocity to the position of these entities but how does that system work and decide if a component is a specific type(dynamic casts?virtual functions in the IComponent?). Would it be better to store components like so :


class Entity
{
public:
std::map<std::string,IComponent*> components;
};
 

So you can give names to components and access them with those names.

I think it could be nice to have a pt2 which goes a bit into implementation details.

April 05, 2013 01:05 AM
Klutzershy

I think it could be nice to have a pt2 which goes a bit into implementation details.

I'm currently working on my own implementation myself. When I figure out the best way to do it, I'll follow up with an article on that.

April 05, 2013 03:06 AM
CC Ricers

I guess your implementation collides a bit with the one I followed in this article, which is why I gave Entities a common interface. But I see your point in why you may not do that, because then it leads back into an inheritance pattern of making all sorts of Entity classes.

Then if you want to really know that a given Entity could be a Monster, other than peeking inside its components, is to give it a generic name as a string. Maybe that's the job of an entity management system, it would see what components are stored in it and then determine, "this is a Monster". And similarly, if any components are replaced/removed, the system would check again.

April 05, 2013 03:21 AM
Klutzershy

The key thing to understand is that there is no notion of a "Monster". An entity can behave like a Monster if it has the correct components, but beyond loading in the data and spawning the entity the systems don't care.

April 05, 2013 06:04 AM
Alpha_ProgDes

I guess your implementation collides a bit with the one I followed in this article, which is why I gave Entities a common interface. But I see your point in why you may not do that, because then it leads back into an inheritance pattern of making all sorts of Entity classes.

Then if you want to really know that a given Entity could be a Monster, other than peeking inside its components, is to give it a generic name as a string. Maybe that's the job of an entity management system, it would see what components are stored in it and then determine, "this is a Monster". And similarly, if any components are replaced/removed, the system would check again.

If I understand correctly, if you wanted the entity to be a "Monster", then you'd give that Entity a "Monster" component. That way, the Entity is only defined by the components given. If the last boss is a zombie pirate with hovers. The Entity (I think) would look like: Entity (boss, zombie, pirate, hover).

Anyone, please feel free to correct any of my misunderstandings.

April 05, 2013 07:42 AM
Michael Tanczos
April 09, 2013 04:41 AM
Seif Haddada

very helpful article , the usage of diagrams make it sample to understand (I found english not easy to understand and words relatide to game developpment make it harder ) If you can make other articles with diagrams for level conception I will be very grateful,

April 09, 2013 12:24 PM
77times

Very helpful article for someone relatively new to this stuff, like me. I've been trying to do an implementation of this in C++, and while I can understand the concept I've been having a fair bit of trouble. One thing that's really been bugging me keeping track of an entity's components. How would you recommend storing an entity's components, and then how would you suggest accessing them from a system?

May 07, 2013 05:25 AM
phil_t

Then if you want to really know that a given Entity could be a Monster, other than peeking inside its components, is to give it a generic name as a string. Maybe that's the job of an entity management system, it would see what components are stored in it and then determine, "this is a Monster". And similarly, if any components are replaced/removed, the system would check again.

There shouldn't ever be any reason to know whether an entity is a monster. If you have code that does "if this is a monster, then....", that means you're doing it wrong.

Of course, for debugging purposes it can be useful to identify the object somehow. The code that created the entity and its components can tag it with some string identifier in that case (or if you're using some sort of prefab system, then the name of the prefab).

May 17, 2013 02:35 AM
smorgasbord

How would you go about implementing a "kill all monsters" type of spell? Would you tag the entity with a monster component? Or, what is the right (/better) way to do such a thing in a component based approach?

<snip>

There shouldn't ever be any reason to know whether an entity is a monster. If you have code that does "if this is a monster, then....", that means you're doing it wrong.

May 24, 2013 08:45 AM
Quasimojo

How would you go about implementing a "kill all monsters" type of spell? Would you tag the entity with a monster component? Or, what is the right (/better) way to do such a thing in a component based approach?

<snip>

There shouldn't ever be any reason to know whether an entity is a monster. If you have code that does "if this is a monster, then....", that means you're doing it wrong.

If I understand correctly, the entities who would be classified as monsters could be tagged with an Enemy component, identifying them as such. The Enemy component could contain an attribute for Threat that could facilitate a more granular sweep by the Attack system.

June 03, 2013 12:28 PM
metsfan

How would systems operate on a collection of components? And what would be a good way to store components? As I undersand from the article components derive from an interface and are stored under entities. This leads to me something like :


class IComponent{};

class Position : public IComponent
{
public:
float x,y;
};

class Velocity: public IComponent
{
public:
float x,y;
};

class Entity
{
public:
std::vector<IComponent*> components;
};
 

Afterwards a Physics system operates on the components of entities to add velocity to the position of these entities but how does that system work and decide if a component is a specific type(dynamic casts?virtual functions in the IComponent?). Would it be better to store components like so :


class Entity
{
public:
std::map<std::string,IComponent*> components;
};
 

So you can give names to components and access them with those names.

I think it could be nice to have a pt2 which goes a bit into implementation details.

If performance is not an issue, this system should work fine.

The problem here, is that using a string map, or even an integer map if you decided to use an enum instead of strings, is that if you are looking up components in a map, and you are doing this many times per frame (in the hundreds or thousands), the performance will suffer big time.

The way I have implemented the entity-component model if performance is important is to use multiple inheritance and use the compentent classes like "mixins". For instance, if you want a Rock to have Sprite and Velocity, per the example in the article, you would do:


class Rock : public SpriteComponent, public VelocityComponent

Now Rock has those getters/setters, and you don't have to do any icky dynamic casting or string map lookups, of course at the cost of the dynamic ability to create new entity types, since now you will have to create a new class definition for each new entity type. Tradeoffs.

June 07, 2013 01:36 AM
Klutzershy

A follow-up to this article has now been posted!

Implementing Component-Entity-Systems

September 29, 2013 06:04 PM
Black Knight

The link to the follow up article is broken.

September 30, 2013 08:35 PM
Klutzershy

It just needs to be peer reviewed.

EDIT: The article has been published and is now available!

October 02, 2013 03:52 PM
ChrisPepper1989

Very concise article, when we created a component system, we hit the wall everyone does where you wonder how components communicate to each other.

We ended up solving this with a post-office-messaging system a component would send a message to a "mailing list". And it would be sent to an entities "post office" (the world was also treated as an entity, so we could send messages to that, the parent or a colliding object etc)

other components could subscribe and unsubscribe to mailing lists.

What was nice about the system was a component didn't have to be finished for you to use its behavior, i.e. you could fire "Play Sound" to the "Audio" mailing list and the game would run without the need to implement any form of sound player. It was very easy to swap components in and out etc, it also played nicely with the A.I. the A.I could listen for messages "Shot At" and translate them into responses for other components "Run Away".

it had less overhead cost of cycling through every component BUT it had the overhead of the over all message handling system. We felt it was a reasonable sacrifice for our game despite the fact it was an RTS and it handled it quite well.

I look forward to reading your follow up article :)

February 19, 2014 04:15 PM
Martin Berger

How would you obtain values from some component which is in some entity. For example your player object has velocity(x,y). How would you obtain this from some random object?

Would you have getVelocity() accessor for every entity which returns x,y if they are there or return component itself?

Or something else? :)

April 10, 2014 01:02 PM
radman5

Thanks so much for this article! Coming from a purely OOP web dev background I have been struggling with game development and this is exactly what I was looking for in my transition into game development :)

March 29, 2017 01:30 AM
Hodgman
Quote

To solve this, game programmers started to build entities through composition instead of inheritance .... This has some major benefits over the object-oriented architecture described above

It's worth pointing out that the original inheritance tree example is not a valid object-oriented architecture. One of the core rules of OO is that you should prefer composition over inheritance!

Almost every single ECS article starts the same way: give an example of an invalid OO design, then fix it with ESC... but they never mention that the original design is actually invalid under OO, and never show what a good OO design would've looked like.

February 28, 2018 02:40 AM
ddengster
7 hours ago, Hodgman said:

It's worth pointing out that the original inheritance tree example is not a valid object-oriented architecture. One of the core rules of OO is that you should prefer composition over inheritance!

Almost every single ECS article starts the same way: give an example of an invalid OO design, then fix it with ESC... but they never mention that the original design is actually invalid under OO, and never show what a good OO design would've looked like.

I think they're just pointing out one of OO's faults when it's first principles are taken too far. And it's first principles are organizing code around the notion of objects and using certain concepts to do so - the composition over inheritance thing is one of the things that feels like a band-aid for OO.

February 28, 2018 10:22 AM
Hodgman
51 minutes ago, ddengster said:

I think they're just pointing out one of OO's faults when it's first principles are taken too far. And it's first principles are organizing code around the notion of objects and using certain concepts to do so - the composition over inheritance thing is one of the things that feels like a band-aid for OO.

The composite reuse principle (composition over inheritance) was common knowledge in the early 90's. There's a strong difference between interface inheritance (abstract base classes / interfaces / "implements") and implementation inheritance (non-abstract base classes / deep hierarchies / "extends"). The former were originally part of OO. The latter were added by OOP languages, and have always been a controversial feature. The fact is that if you have these kinds of deep inheritance trees in your code, OO says that your design is wrong - you're writing code with OOP language features, but it is not an OO design.

In the 90's OOP was the popular fad, so a million people suddenly wanted to learn it but only a few were qualified to teach it, so everyone learned bullshit like this -- let's make deep hierarchies and use implementation inheritance everywhere!! The fact that it was an immensely widespread practice (and now an immensely regretted practice) doesn't change the fact that OO has always said "don't do this!", but no one at the time bothered to listen.

The ECS bandwagon is so strong because incorrect OOP was so prevalent... however, I only take issue because simply saying "You've been using OOP incorrectly this whole time, so throw it all away and use ECS instead!" serves to cover up a very important opportunity for learning. They should instead preach "You've been using OOP incorrectly this whole time, so go read the actual OO pillars and throw out all your bad habits! By the way, another composition based design framework is ECS, which wouldn't even let you use those bad habits if you tried!".

While I'm at it though... ECS is heavily based on relational modeling, which has a very strong theoretical basis behind it dating back earlier than (real) OO too, but ECS articles always fail to mention it. When people are asking questions like "which component should own which data", relational's formal rules give solid answers -- e.g. you can tell them to arrange their component's members into third normal form...

So instead of ECS articles using incorrect-OO examples as a straw-man against OO, they should actually be informing programmers that they don't know OO and they don't know relational and maybe they should think about learning these things, because after all, ECS is built on top of them!

February 28, 2018 10:56 AM
bogdan9

Very nicely wrote, it's the first time I read about this and I understood it very easily.

November 29, 2019 11:39 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

The traditional way to implement game entities was to use object-oriented programming. This led to large, rigid class hierarchies. To solve this, game programmers started to build entities through composition instead of inheritance.

Advertisement
Advertisement