Designing a component based game

Emiliano
  • Designing a component based game Emiliano

    I'm writing a shooter (like 1942, classic 2D graphics) and I'd like to use a component based approch. So far I thought about the following design:

    1. Each game element (airship, projectile, powerup, enemy) is an Entity

    2. Each Entity is a set of components which can added or removed at run-time. Examples are Position, Sprite, Health, IA, Damage, BoundingBox etc.

    The idea is that Airship, Projectile, Enemy, Powerup are NOT game classes. An entity is only defined by the components it owns (and that can change during time). So the player Airship starts with Sprite, Position, Health and Input components. A powerup has the Sprite, Position, BoundingBox. And so on.

    The main loop manages the game "physics", i.e. how the components interact each other:

    foreach(entity (let it be entity1) with a Damage component)
        foreach(entity (let it be entity2) with a Health component)
        if(the entity1.BoundingBox collides with entity2.BoundingBox)
        {
            entity2.Health.decrease(entity1.Damage.amount());
        }
    
    foreach(entity with a IA component)
        entity.IA.update(); 
    
    foreach(entity with a Sprite component)
        draw(entity.Sprite.surface()); 
    
    ...
    

    Components are hardcoded in the main C++ application. Entities can be defined in an XML file (the IA part in a lua or python file).

    The main loop doesn't care a lot about entities: it only manages components. The software design should allow to:

    1. Given a component, get the entity it belongs to

    2. Given an entity, get the component of type "type"

    3. For all entities, do something

    4. For all entity's component, do something (e.g: serialize)

    I was thinking about the following:

    class Entity;
    class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
    class Sprite : public Component {...};
    class Position : public Component {...};
    class IA : public Component {... virtual void update() = 0; };
    
    // I don't remember exactly the boost::fusion map syntax right now, sorry.
    class Entity
    {
       int id; // entity id
       boost::fusion::map< pair<Sprite, Sprite*>, pair<Position, Position*> > components;
       template <class C> bool has_component() { return components.at<C>() != 0; }
       template <class C> C* get_component() { return components.at<C>(); }
       template <class C> void add_component(C* c) { components.at<C>() = c; }
       template <class C> void remove_component(C* c) { components.at<C>() = 0; }
       void serialize(filestream, op) { /* Serialize all componets*/ }
    ...
    };
    
    std::list<Entity*> entity_list;
    

    With this design I can get #1, #2, #3 (thanks to boost::fusion::map algorithms) and #4. Also everything is O(1) (ok, not exactly, but it's still very fast).

    There is also a more "common" approch:

    class Entity;
    class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
    class Sprite : public Component { static const int type_id = 0; };
    class Position : public Component { static const int type_id = 1; };
    
    class Entity
    {
       int id; // entity id
       std::vector<Component*> components;
       bool has_component() { return components[i] != 0; }
       template <class C> C* get_component() { return dynamic_cast<C> components[C::id](); } // It's actually quite safe
    ...
    };
    

    Another approch is to get rid of the Entity class: each Component type lives in its own list. So there is a Sprite list, a Health list, a Damage list etc. I know they belong to the same logic entity because of the entity id. This is simpler, but slower: the IA components needs access basically to all other entity's components and that would require searching each other component's list at each step.

    Which approch do you think is better? is boost::fusion map suited to be used in that way?

  • if i were to write such a code i would rather ot use this approch (and i'm not using any boost if it's important for you), since it can do all the thing you want but the problem is when there are too many enteties which do not share some componnet, finding those which have it will consume some time. other than that there is no other problem i can thing of :

    // declare components here------------------------------
    class component
    {
    };
    
    class health:public component
    {
    public:
        int value;
    };
    
    class boundingbox:public component
    {
    public :
        int left,right,top,bottom;
        bool collision(boundingbox& other)
        {
            if (left < other.right || right > other.left)
                if (top < other.bottom || bottom > other.top)
                    return true;
            return false;
        }
    };
    
    class damage : public component
    {
    public:
        int value;
    };
    
    // declare enteties here------------------------------
    
    class entity
    {
        virtual int id() = 0;
        virtual int size() = 0;
    };
    
    class aircraft :public entity, public health,public boundingbox
    {
        virtual int id(){return 1;}
        virtual int size() {return sizeof(*this);};
    };
    
    class bullet :public entity, public damage, public boundingbox
    {
        virtual int id(){return 2;}
        virtual int size() {return sizeof(*this);};
    };
    
    int main()
    {
        entity* gameobjects[3];
        gameobjects[0] = new aircraft;
        gameobjects[1] = new bullet;
        gameobjects[2] = new bullet;
        for (int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                if (dynamic_cast<boundingbox*>(gameobjects[i]) && dynamic_cast<boundingbox*>(gameobjects[j]) &&
                    dynamic_cast<boundingbox*>(gameobjects[i])->collision(*dynamic_cast<boundingbox*>(gameobjects[j])))
                    if (dynamic_cast<health*>(gameobjects[i]) && dynamic_cast<damage*>(gameobjects[j]))
                        dynamic_cast<health*>(gameobjects[i])->value -= dynamic_cast<damage*>(gameobjects[j])->value;
    }
    

    in this approch every component is a base for an entity so given the component it's pointer is also an entity! the second thing you ask for is to have a direct access to some entity's components eg. when i need to access damage in one of my entities i use dynamic_cast<damage*>(entity)->value, so if entity has damage component it will return the value. if you are not sure whether entity has component damage or not you can easily check if (dynamic_cast<damage*> (entity)) return value of dynamic_cast is always NULL if the cast is not valid and a same pointer but with the requested type if it's valid. so to do something with all the entities which has some component you can do it like below

    for (int i=0;i<enteties.size();i++)
        if (dynamic_cast<component*>(enteties[i]))
            //do somthing here
    

    if there any other questions i'll be glad to answer.

  • I've found that component-based design and data-oriented design go hand in hand. You say that having homogeneous lists of components and eliminating the first-class entity object (instead opting for an entity ID on components themselves) will be "slower", but that's neither here nor there since you haven't actually profiled any real code that implements both approaches to arrive at that conclusion. As a matter of fact, I can almost guarantee you that homogenizing your components and avoiding the traditional heavy virtualization will be faster due to the various advantages of data-oriented design -- easier parallelization, cache utilization, modularity, etc.

    I'm not saying this approach is ideal for everything, but component systems which are basically collections of data that need the same transformations performed upon them every frame, simply scream to be data-oriented. There will be times when components need to communicate with other components of different types, but this is going to be a necessary evil either way. It shouldn't drive the design, however, since there are ways to solve this issue even in the extreme case that all components are processed in parallel such as message queues and futures.

    Definitely Google around for data-oriented design as it relates to component-based systems, because this topic comes up a lot and there is quite a bit of discussion and anecdotal data out there.

Tags
c++ design-patterns component-based
Related questions and answers
  • /////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Sprite { private: string name; char symbol; float shield; int location[2]; bool alive; public: ///////////////////// Get and SET all the privates... in the main function * not Finally.. but i believe that te numb_coll problem has something to do with the time and framrate. When running full speed i quickly die upon entering a wall as numb_coll...--; cspace++;}} for(int s= 0; s &lt;sspace; s++) buff[buff.size()-1].push_back(' '); // cwall2 //if(cwall2!= 0){ // if(cwall2&gt;0

  • Dear all, this is going to be tough: I have created a game object factory that generates objects of my wish. However, I get memory leaks which I can not fix. Memory leaks are generated by return new Object(); in the bottom part of the code sample. static BaseObject * CreateObjectFunc() { return new Object(); } How and where to delete the pointers? I wrote bool ReleaseClassType(). Despite... vector container. I have chosen this way because I parse an object map file. I have object type IDs (integer values) which I need to translate them into real objects. Because I have over 100

  • code in them... Right now for testing purposes, I just have the main loop call Level1 level1; and use the functions, but when I run the game I get a segmentation fault. This is the first time I've... { protected: Mix_Music *music; SDL_Surface *background; SDL_Surface *background2; vector<Enemy> enemy; bool loaded; int time; public: Level(); virtual ~Level...I'm trying to write my level classes by having a base class that each level class inherits from...The base class uses pure virtual functions. My base class is only going to be used as a vector

  • // of Resource. Note it only hands give a const reference to the Resource, as // it is read only. template &lt;class T> class ResourceGuard { public: ResourceGuard(T *_resource): resource(_resource) { resource-&gt;reference(); } virtual ~ResourceGuard() { resource-&gt;dereference();} const T* operator*() const { return (resource); } }; class ResourceManager... counting, it only counts when the resource is being read, (so the reference count may be 0, but an entity might still be keeping track of it's uid). It is also possible to mark resources for loading well

  • ) is therefore highly wasteful. Then again, making each entity state a singleton hardly seems appropriate. What do you suggest I do? (The same can be said about game states, so whatever the solution to this is, I guess it can be applied to game states as well.) 2. The state of the entity sprite. Most of the time, whenever the state of the entity changes, the sprite that is used to represent it must... it does it like this: void Entity::handleEvent(const Event& event) { // We pass this to the state so it could act upon the data in // the entity in a manner appropriate for the given event

  • Components. 3: Component registers itself in GameSubsystem(s). We know at compile-time that there is a GameSubsystemRenderer, so let's ComponentImageRender will call something like... GameSubsystems) can implement registerComponent(Component*). pro. Components and GameSubystems know nothing about each other. con. In C++ it would look like ugly and slow typeid-switch...I'm creating a component-based game object system. Some tips: GameObject is simply a list of Components. There are GameSubsystems. For example, rendering, physics etc. Each GameSubsystem contains

  • sort of designs other people used to overcome them, or would use. For a little background, I started working on it in my free time last summer. I was initially making the game in C#, but about 3 months... hook into those delegates to enforce their effects. In C++, I did something similar. Obviously there's no direct equivalent to C# delegates, so instead I created something like this: typedef boost... question about it is, is this a worthwhile approach at all? If it's a bad design I want to know now before I invest too much more time making all the screens I'm going to need. How do you build up

  • (); missile-&gt;Init(true, s-&gt;X, s-&gt;Y); bullets-&gt;push_back(missile); } } void CleanUp() { for(unsigned int index = 0; index &lt; bullets-&gt;size(); index...;at(index)-&gt;Kill(); } } } void DrawBullets(BITMAP* buffer, BITMAP* sprite) { for(unsigned int index = 0; index &lt; bullets-&gt;size(); index++) { if (bullets-&gt;at(index)-&gt;Alive == true) { bullets-&gt;at(index)-&gt;Draw(buffer, sprite); } } } //Entry point of the application int main(void) { Ship* s = new Ship(); int x

  • physics properties. I know there will be errors here as I get the items colliding with the ground but not with each other properly. So I would appreciate some input on what I am doing wrong. void... the rigid body object btRigidBody *RigidBody = new btRigidBody(0, MotionState, Shape, LocalInertia); // Store a pointer to the Ogre Node so we can update it later RigidBody-&gt;setUserPointer((void... every frame to handle input and render etc I call an UpdatePhysics method to update the physics simulation. void GameState::UpdatePhysics(unsigned int TDeltaTime) { World-&gt;stepSimulation(TDeltaTime

Data information