Jump to content

  • Log In with Google      Sign In   
  • Create Account


Basic Component Based Entity


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
68 replies to this topic

#21 VReality   Members   -  Reputation: 436

Like
0Likes
Like

Posted 02 November 2011 - 06:10 PM

how can I tear down these components and their relationship to an entity id? Do I just look over all subsystems that are in the subsystem registry and notify them that entity ABC should be removed?


That's the easy way. And in fact entity death is infrequent enough that you should probably start with that, and you'll never find a reason to change it.

If you decide that you can't stomach potentially wasted searches in subsystems which don't happen to have a component for the entity in question, then you need a record of which components make up an entity. That could be maintained in an actual entity object.

Or if you want to get clever(?) you could go ahead and make the call on all sub-systems, but have them cache a list of entities to remove, and perform the removal during their normal processing of their components (you might have to do this sort of thing anyway, if you're multi-threading). That eliminates the extra searches. And that list will usually be empty, and rarely have more than one entry.

Sponsor:

#22 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 02 November 2011 - 06:11 PM

Ok brace yourselves for a possibly horrible idea.

This is an idea on how to create communication between subsystems. Create a vector that contains Event structures and pass this vector to each subsystem through the update function. If it's something solely AI specific remove the event after the AI system updates. Do the same for the other events. At the end up the game loop empty out the event vector.
Horrible, bad, ok?

When my AI controlled entities die they are going to just fizz out (the game takes place in a virtual reality type thing) and after that's done I'm just going to remove the components from the systems based on the entity ID.


Not a horrible idea; I believe it actually has merit but with a slight difference in implementation.

I have seen where games have had a message dispatcher in their game loop, typically at the beginning. This allows systems to queue up events, whether they're for immediate delivery or queued for delay delivery and once every game logic loop, the event vector is traversed and events dispatched. This is where I believe you could look into signal/slots or delegate type patterns where the subsystems can register themselves with your message dispatcher when they initialize. When an event is popped off the event vector during the message dispatcher loop, it can be dispatched to the appropriate subsystems and only those who care about that event. The benefit here is you traverse the event list once, inform only the parties who care and you are free to modify your game loop update logic in your other systems without concern with how that may impact your message/notification system.

#23 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 02 November 2011 - 06:26 PM

So what if I did this:
Assuming that I will know what subsystems exist during compile time. How about having the event dispatching sort through the "main" event vector producing vectors with events only pertaining to what the subsystem needs? So the AI subsystem would only get collision, sound, and maybe positional cues?

And for delayed events I'll just have the dispatcher wait to put them in the arrays until a certain amount of game loops have passed or until an outside source says to.

#24 AllEightUp   Moderators   -  Reputation: 4169

Like
0Likes
Like

Posted 02 November 2011 - 06:34 PM

how can I tear down these components and their relationship to an entity id? Do I just look over all subsystems that are in the subsystem registry and notify them that entity ABC should be removed?

That's the easy way. And in fact entity death is infrequent enough that you should probably start with that, and you'll never find a reason to change it.

If you decide that you can't stomach potentially wasted searches in subsystems which don't happen to have a component for the entity in question, then you need a record of which components make up an entity. That could be maintained in an actual entity object.

Or if you want to get clever(?) you could go ahead and make the call on all sub-systems, but have them cache a list of entities to remove, and perform the removal during their normal processing of their components (you might have to do this sort of thing anyway, if you're multi-threading). That eliminates the extra searches. And that list will usually be empty, and rarely have more than one entry.

Even if you are not multithreaded it is usually rather important to keep entities/components alive until frame processing is complete. The simple answer to the example I'm going to give is "don't do it" but it happens all the damned time and is usually a tricky bug to figure out... Say you have all the components executing in different systems and one entities set of components says: hey, I wanna kill that guy and tells the "turret track component" which executes later to start tracking an object. If that object disappears before the turret track component updates, unless you are using a handle system or the various smart/weak pointers correctly, boom, a random and hard to reproduce crash is likely to ensue when the turret de-references the object pointer. I'll always suggest the "pending death" flag (or system list) on a component instead of trying to do real time cleanup properly because people do get in hurries and forget about the implications of not passing a handle and/or weak_ptr etc.

Given that the type of bug mentioned won't show up in 99.9% of uses, that point 1% leads to ulcers, hair loss and massive screaming, crying and hiding under desks....

#25 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 02 November 2011 - 06:37 PM

So what if I did this:
Assuming that I will know what subsystems exist during compile time. How about having the event dispatching sort through the "main" event vector producing vectors with events only pertaining to what the subsystem needs? So the AI subsystem would only get collision, sound, and maybe positional cues?

And for delayed events I'll just have the dispatcher wait to put them in the arrays until a certain amount of game loops have passed or until an outside source says to.


That's obviously one way to approach it but even though you know what subsystems you have at compile time, I believe in trying to keep some level of separation in my code.

What i mean is that today one subsystem wants to know about two events. A month from now, you enhance your code and now that subsystem needs to know about another event. Not only do you need to modify the subsystem accordingly, but you have to muck with the dispatcher. If you leave the responsibility of the subsystem to dictate to the dispatcher what it wants to know about and your dispatcher smart enough to decipher the needs of subsystems based on the context of the events, the dispatcher should never have to be changed unless you're making a significant change possibly to your event structure.



#26 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 02 November 2011 - 07:21 PM

That's a great a point.

This is driving me up a wall:
CEventDispatcher.h

#ifndef CEVENTDISPATCHER_H
#define CEVENTDISPATCHER_H
#include <vector>

#include "Event.h"
#include "IEventHandler.h"

using namespace std;

//class IEventHandler;

class CEventDispatcher
{
    public:
        virtual ~CEventDispatcher();

        static CEventDispatcher* inst();
        // Adds an event handler to the vector list
        void addHandler(IEventHandler* handler);

        // Sends an event to all of the event handlers
        void sendEvent(Event* event);

        //Clears handler list
        void clearHandlers();
        void removeHandler(IEventHandler* handler);

    protected:
        CEventDispatcher();
    private:
        static CEventDispatcher* pInstance;
        vector<IEventHandler*> handlerList;
};

#endif // CEVENTDISPATCHER_H


CEventDispatcher.h
#include "CEventDispatcher.h"

CEventDispatcher* CEventDispatcher::pInstance=0;

CEventDispatcher::CEventDispatcher()
{
    //ctor
}

CEventDispatcher::~CEventDispatcher()
{
    //dtor
}

CEventDispatcher* CEventDispatcher::inst()
{
    if (pInstance == 0)
    {
        pInstance = new CEventDispatcher();
    }
    return pInstance;
}

void CEventDispatcher::addHandler(IEventHandler* handler)
{
    handlerList.push_back(handler);
}

void CEventDispatcher::sendEvent(Event* event)
{
    for (unsigned int i=0;i<handlerList.size();i++)
    {
        handlerList[i]->handleEvent(event);
    }
}


EventHandler.h


#ifndef IEVENTHANDLER_H
#define IEVENTHANDLER_H

#include "CEventDispatcher.h"
#include "Event.h"



class IEventHandler {

public:
  virtual void handleEvent(const Event* e) = 0;

  IEventHandler() {
    // Self-Register with the event dispatcher upon creation
    CEventDispatcher::inst()->addHandler(this);
  }
};

#endif // IEVENTHANDLER_H

Event.h
#ifndef CEVENT_H
#define CEVENT_H
enum EventType
 {
   // Mouse button events. Each of the next six events gets passed
   // the absolute position of the mouse
   E_MOUSELEFTBUTTONPRESS,
   E_MOUSELEFTBUTTONRELEASE,

   // Start new games
   E_NEWGAMEEASY,
   E_NEWGAMENORMAL,
   E_NEWGAMEHARD,

   // Game play related
   E_INCREMENTSCORE,
   E_PAUSEGAME,
   E_CONTINUEGAME,
   E_GAMEOVER,

   // Close the App
   E_APPCLOSE
 };

 struct Event
 {
   EventType Type;
   void* Data;
 };
#endif


I keep getting a compiler error saying that CEventDispatcher has not been declared in IEntityHandler.h. I've tried variations on include files but can't get anything to compile. I'm trying to set CEventDispatcher as a singleton. I've probably set the singleton up wrong this is my first try at one.

#27 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 02 November 2011 - 09:35 PM

I believe it is your circular dependencies in your header files between IEventHandler.h and the CEventDispatcher.h. If you uncomment your class IEventHandler; statement and then remove the #include "IEventHandler.h" in your event dispatcher header, it should compile. Since you aren't using IEventHandler in your dispatcher header except as a holder reference, you can simply forward declare it since your handler header is actually including the dispatcher header so you can self-register :).

#28 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 02 November 2011 - 09:43 PM

That was one of the things I tried and it would complain about the handleEvent function not being declared.

#29 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 02 November 2011 - 09:57 PM

u want to get clever(?) you could go ahead and make the call on all sub-systems, but have them cache a list of entities to remove, and perform the removal during their normal processing of their components (you might have to do this sort of thing anyway, if you're multi-threading). That eliminates the extra searches. And that list will usually be empty, and rarely have more than one entry.



Nice idea; didn't consider that approach.

Therefore, given subsystem determines a specific entity should be removed; such as our CorpseSubsystem. It invokes the EntitySubsystem::queueEntityForRemoval(). This adds the entity id to a pending removal list. The last step in the main game loop immediately after render() is to invoke the entity system's update() method. Here, I could traverse this pending removal list and then issue the request to all other subsystems to remove that entity's components. The benefit here is that if dynamic components are created for that entity during it's life cycle, by issuing a delete to all subsystems, I insure that all references are adequately removed and returned to object pools. These subsystems move that entity's components to a pending list and then the first step that each subsystem does at the beginning of their update() during the next loop iteration is to remove these stale objects and return them to their object pool for reuse later.

#30 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 02 November 2011 - 09:59 PM

That was one of the things I tried and it would complain about the handleEvent function not being declared.


Have you created a class that derives from IEventListener yet? If you have, did you implement that pure virtual method? That type of error to me sounds as though it's complaining about that scenario rather than the relationship between the interface class and the dispatcher. I put your code into Visual Studio 2010 here based on what you provided and it compiled perfectly once I made the tweak I indicated.

#31 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 02 November 2011 - 10:28 PM

Let us assume we have 3 types of components: spatial, mesh, and render. The spatial holds my position, the mesh contains a list or reference to a mesh for the entity, and the render is where the actual rendered content is maintained in the scene graph via a render scene node.

I assume that my spatial subsystem would create a scene node at the specified position in the spatial components. As the component's position changes, the spatial update() would reflect those changes to the referenced scene node. The mesh component would load whatever mesh information from the resource system and prepare it for the rendering phase. The render component would request a reference to both of these components (mesh/spatial) during initialization and then during the render system's update() loop, the we'd check if a scene node for the mesh has been added as a child to the scene node referenced by the spatial component. If so and the mesh has changed, the render component would update the mesh on the render component's child scene node. If a child scene node hasn't been created, the render system creates one off the spatial component's node, and then applies the specified mesh. Am I on track?

#32 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 03 November 2011 - 06:59 AM

I'm using MinGW so that might be my problem because I made the changed you've said and added a class that inherits the handleEvent function but no go..

#33 crancran   Members   -  Reputation: 373

Like
0Likes
Like

Posted 03 November 2011 - 07:49 AM

I'm using MinGW so that might be my problem because I made the changed you've said and added a class that inherits the handleEvent function but no go..

Show me what that inherited class looks like.

#34 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 03 November 2011 - 07:51 AM

#ifndef CSTATEMACHINE_H
#define CSTATEMACHINE_H
#include "IEventHandler.h"

#include "CState.h"
#include "CMenuState.h"
#include "CGameState.h"


class CStateMachine: public IEventHandler
{
    public:
        CStateMachine();
        virtual ~CStateMachine();

        void switchState();
        void init();
        void update();
        void draw();

        void handleEvent(const Event* e);

        CState* state;
    protected:
    private:

};

#endif // CSTATEMACHINE_H

#include "CStateMachine.h"


CStateMachine::CStateMachine()
{
}

CStateMachine::~CStateMachine()
{
    //dtor
}

void CStateMachine::switchState()
{

}

void CStateMachine::init()
{
    state = new CMenuState();
    state->add();
}

void CStateMachine::update()
{
    if (state!=NULL)
    {
        state->update();
    }
}

void CStateMachine::draw()
{
    if (state!=NULL)
    {
        state->draw();
    }
}

void CStateMachine::handleEvent(const Event* e)
{

}



Hot pancakes and syrup I got it. I added #include "IEventHandler.h" to CEventDispatcher.cpp and it compiled.

#35 smr   Members   -  Reputation: 1615

Like
0Likes
Like

Posted 03 November 2011 - 08:18 AM


Just a word of caution: ...


Isn't this where being able to build templates in the game editor would play a vital role?


Yes, templates would make things easier for the designer, but what about the developer? Let's take your example of separating vital stats out into each their own subsystem and component type: HealthComponent, WeaponComponent, ArmorComponent, StrengthComponent, DexComponent, AttackComponent, etc. What would combat look like for an entity like this?

I attempted to provide a code sample, but it was so complicated that I didn't think I would be able to accomplish it without actually opening up my IDE and formally coding something. I can say it would involve a lot of asking systems if the two entities involved had certain stat components, passing messages around to receive damage, cooperation between stat components to try to figure out how much damage should be taken, whether a hit is even made, etc. It just seems like adding complexity just because you CAN and because somewhere down the line someone told you that you SHOULD loosely couple EVERYTHING. Loose coupling is great but loose coupling for the sake of loose coupling is just another way to shoot yourself in the foot.

#36 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 03 November 2011 - 10:17 AM

I attempted to provide a code sample, but it was so complicated that I didn't think I would be able to accomplish it without actually opening up my IDE and formally coding something. I can say it would involve a lot of asking systems if the two entities involved had certain stat components, passing messages around to receive damage, cooperation between stat components to try to figure out how much damage should be taken, whether a hit is even made, etc. It just seems like adding complexity just because you CAN and because somewhere down the line someone told you that you SHOULD loosely couple EVERYTHING. Loose coupling is great but loose coupling for the sake of loose coupling is just another way to shoot yourself in the foot.


I was thinking the same thing myself. To me a single vital's component would be best. While my game isn't an RPG neither does it have a complex stats system but even if it was an RPG don't 90% of entities that might interact in away that involves vitals all have basically the same set of stats, given different values of course but that should be taken care of with the factory that put the entity together in the first place potentially with a data table or an external Init script (What I plan on using)

My whole purpose of using this system is to get away from hardcoding my entities so I don't have recompile when I want to add a new type making it easier.

Like when I parse my level and encounter an AnimatedMeshSceneNode (The mesh type I'm going to make all entities out of) I want it to extracted the name from the node (Yay Irrlicht!) and use that to build the entity by loading the external scripts with the same name. I also want to be able to just give the factory a name to manually create an entity if I need too.

I assume that my spatial subsystem would create a scene node at the specified position in the spatial components. As the component's position changes, the spatial update() would reflect those changes to the referenced scene node. The mesh component would load whatever mesh information from the resource system and prepare it for the rendering phase. The render component would request a reference to both of these components (mesh/spatial) during initialization and then during the render system's update() loop, the we'd check if a scene node for the mesh has been added as a child to the scene node referenced by the spatial component. If so and the mesh has changed, the render component would update the mesh on the render component's child scene node. If a child scene node hasn't been created, the render system creates one off the spatial component's node, and then applies the specified mesh. Am I on track?



To me it seems too broken up. If I may ask why not just use the entity's scene node directly as a way to store and access the positional and rotational data? Do everything directly it? Removing the need for a spatialdatabase by just having a subsystem that contains all the entity node components with the given IDs?


I also want to thank everyone who has responded. This has probably been one of the most fun topics I've been in on a forum.

#37 VReality   Members   -  Reputation: 436

Like
0Likes
Like

Posted 03 November 2011 - 12:27 PM

separating vital stats out into each their own subsystem and component type: HealthComponent, WeaponComponent, ArmorComponent, StrengthComponent, DexComponent, AttackComponent, etc. What would combat look like for an entity like this?


To me a single vital's component would be best. ...don't 90% of entities that might interact in away that involves vitals all have basically the same set of stats


If we think about componentized entities as being a data oriented approach, the guiding principal is to separate the entity into chunks of data which will be processed together.

If we envision all those stats being used for the purpose of producing a feature (like combat) through some sort of complex interaction, then we're envisioning processing all the stats together, so they should form a component. Breaking each stat into it's own component is a violation of data oriented design. Well formed components make it relatively easy to write the code, as well as letting that code run more efficiently.

Now if you find that you have various different types of entities which actually only need subsets of those stats, then you'll find it pretty difficult to code those interactions which process all the stats together, because those interactions would make no sense for entities which you've decided don't need those stats. In that case, you've identified some subsets of the stats which should form smaller components.

Rather than thinking of it as data oriented, I like to think of it as feature oriented. Entities have features, or aspects, or abilities, etc., like the ability to be placed in the 3d game world, to be rendered, to animate, to participate in a physics simulation, to collide with other entities, to give and receive damage, etc. Each of these features involve it's own (mostly) unique data, and the code which operates on it. In general, a component should contain the data (specific to a given entity) which a subsystem processes. If it doesn't make sense to create two subsystems, it probably doesn't make sense to create two component types.

One area of stickiness is when multiple features are equally dependent on a common piece of data (e.g., AI, physics, and rendering all depending on entity position). The only two approaches that come to mind for dealing with this are either to factor that data out to a separate component (which breaks data orientation, forcing components to look it up while processing), or to duplicate that data (forcing components to synchronize it, and opening the door to copies being out of sync). At the moment, I tend to lean heavily towards the first approach, especially in the case of entity position, since there is a sub-system which makes sense for dealing with position data only.

#38 jaeg   Members   -  Reputation: 165

Like
0Likes
Like

Posted 03 November 2011 - 03:35 PM

Should I make separate classes for the subsystems or could I just have a single subsystem class that has functions that let you decide what events they should receiver for the components they update?

#39 crancran   Members   -  Reputation: 373

Like
1Likes
Like

Posted 03 November 2011 - 05:01 PM

Short answer, they should be different.

Whenever you are coding a particular class/object, you should always refer to two principles.

The first is called Separation of Concerns. This principle is often used when it becomes evident that an object needs to be re-factored into smaller distinct features that should overlap as little as possible. The term concern can be defined as a focus, interest, feature, or behavior depending upon your context. The second is called Single Reponsibility Principle. This principle basically outlines that every object/class should have a single responsibility, and that all its services (methods/functions) should be narrowly aligned with that responsibility.

So if we think back to what we're trying to do with a component-based design, we are trying to isolate and narrowly defined set of functionality or feature/behavior into classes that are as self-contained within reason. This way should we need to change the behavior of a component and its managing subsystem, we can with as little risk or need to decipher additional lines of code that have no relevance to the feature we're trying to enhance or bugfix :).

Those two principles should always be relied upon and you will find it is much easier to make modifications over the life cycle of any programming project. Of course it's my opinion and others mileage may vary in how strongly they feel about those two principles. :).

#40 Madhed   Crossbones+   -  Reputation: 2732

Like
0Likes
Like

Posted 03 November 2011 - 06:02 PM

Short answer, they should be different.

Whenever you are coding a particular class/object, you should always refer to two principles.

The first is called Separation of Concerns. This principle is often used when it becomes evident that an object needs to be re-factored into smaller distinct features that should overlap as little as possible. The term concern can be defined as a focus, interest, feature, or behavior depending upon your context. The second is called Single Reponsibility Principle. This principle basically outlines that every object/class should have a single responsibility, and that all its services (methods/functions) should be narrowly aligned with that responsibility.

So if we think back to what we're trying to do with a component-based design, we are trying to isolate and narrowly defined set of functionality or feature/behavior into classes that are as self-contained within reason. This way should we need to change the behavior of a component and its managing subsystem, we can with as little risk or need to decipher additional lines of code that have no relevance to the feature we're trying to enhance or bugfix :).

Those two principles should always be relied upon and you will find it is much easier to make modifications over the life cycle of any programming project. Of course it's my opinion and others mileage may vary in how strongly they feel about those two principles. :).


Exactly! I think these two principles pretty much define a component.
Does any one know why the inheritance obsession became so widespread that the new term "component based entity" had to be invented?

I personally blame online tutorials. :D

EDIT: I just noticed this might be misread. No offense intended. I'm just wondering why there seem to be so many paradigm shifts in game development.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS