Jump to content
  • Advertisement
  • 04/02/13 07:37 PM

    Understanding Component-Entity-Systems

    General and Gameplay Programming

    Klutzershy
    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)


      Report Article


    User Feedback




    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.

    Share this comment


    Link to comment
    Share on other sites

    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.

    Share this comment


    Link to comment
    Share on other sites

    It just needs to be peer reviewed.

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

    Share this comment


    Link to comment
    Share on other sites

    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 :)

    Share this comment


    Link to comment
    Share on other sites

    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? :)

    Share this comment


    Link to comment
    Share on other sites

    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 :)

    Share this comment


    Link to comment
    Share on other sites
    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.

    Share this comment


    Link to comment
    Share on other sites
    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.

    Edited by ddengster

    Share this comment


    Link to comment
    Share on other sites
    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!

    Share this comment


    Link to comment
    Share on other sites



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!