View more

View more

View more

Image of the Day Submit

IOTD | Top Screenshots

The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

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.

68 replies to this topic

#1jaeg  Members

Posted 02 November 2011 - 06:33 AM

So I've been working on a simple component based entity system so I can create entities on the fly without have to have extreme levels of inheritance. This way if I want an entity to be the player all I have to do is add the appropriate components to let it operate under keyboard control. Another reason I want to try this system out so I can have my AI be able to target anything that has a "targetable component". This way they can destroy props in their way to get to the player.
This is the basic system so far:
CEntity class

class CEntity
{
public:
CEntity();
virtual ~CEntity();

CComponent* getComponentbyName(string);

void update();
protected:
private:
vector<CComponent*> components;
};

CEntity::CEntity()
{
//ctor
}

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

{
components.push_back(newComponent);
}

CComponent* CEntity::getComponentbyName(string name)
{
for (unsigned int i=0;i<components.size();i++)
{
if (name.compare(components[i]->name)==0)
{
return components[i];
}
}
return 0;
}

void CEntity::update()
{
for (unsigned int i=0;i<components.size();i++)
{
components[i]->update();
}
}


CComponent:

class CComponent
{
public:
string name;

CComponent();
virtual ~CComponent();
virtual void update()=0;

protected:
private:
};


It's not game specific right now since I was just cranking it out last night to see if it even work. The entity will also contain an Irrlicht IAnimatedMeshScene node as well.
I have a factory designed that'll let me create entities based on a name you provide where it'll then proceed to load an external script informing it what components the entity will need. There is also a function that'll create it based on it's node. This is mostly so I can use my level editor which lets me give nodes names. The function takes the node and extracts the name and precedes like the former function.

Is this idea valid or should I be looking at a different way to approach this? The code is really simple right now because I just wanted to get the basic mechanism working before I integrate it into my game engine.

#2Naros  Members

Posted 02 November 2011 - 07:08 AM

This is certainly a decent first pass; however here are some suggestions.

First, rather than iterating over your game entities and their components in a heterogeneous fashion, why not consider a more homogenized way. Essentially your components are also maintained by a secondary object called a subsystem. Typically subsystems manage a particular type of component and so there is somewhat of a 1-to-1 relationship. This subsystem holds a reference to each component that was created that it manages in an internal vector and when your game loop iterates, you iterate like components together. This also allows you to perform updates on your components in an ordered fashion, regardless of how they may have been inserted into your entities component vector during creation time.

class Entity
class BaseComponent
class BaseSystem
class RenderComponent : public BaseComponent
class RenderSystem : public BaseSystem

void CGameEngine::update(void timeSinceLastFrame) {
m_System1.update(timeSinceLastFrame);
m_System2.update(timeSinceLastFrame);
m_AnimationSystem.update(timeSinceLastFrame);
m_PhysicsSystem.updatePass1(timeSinceLastFrame);
m_System3.update(timeSinceLastFrame);
m_PhysicsSysetm.updateFinal(timeSinceLastFrame);
m_RenderSystem.update(timeSinceLastFrame);
};


As you can see above, our physics system (only as an example) performs two passes over it's components, and it could be that the system manages two different types of components as well but they need to be updated in a specific order. This approach allows us to step those entities in order. Additionally, if one subsystem finds an entity should be placed in a 'dont update me so often' state, that subsystem can move that component from one vector to another and maybe only update that secondary vector after a particular amount of time has passed while the remaining components are updated with each loop iteration.

This modular approach makes it easy to change and maintain logic and functionality in it's own little area of the world without imposing logic on your entity class during its update() method or your entity manager class during its update() method. To boot, this design typically is far more performing too.

HTH

#3jaeg  Members

Posted 02 November 2011 - 07:58 AM

So basically if I was to derive AIcomponent from BaseComponent and a AISystem from BaseSystem and then gave (for example) each zombie an AI component instead of having the zombies update their AIcomponent themselves have the AISystem do it for them?

Also would it be better then during cleanup from one level to the next to delete the AISystem are create a new one or to just simply empty the vector and deleting the components after the entities have been deleted?

#4smr  Members

Posted 02 November 2011 - 08:03 AM

Yes, that's generally how it's done. It's the a very practical way to ensure that the various systems are updated in the correct order.

#5jaeg  Members

Posted 02 November 2011 - 10:40 AM

Ok, now is it practical to give components some components themselves? I'm mostly thinking about the AI components. Like being able to give an AI component another component that allows it to receive and processes sound events and the like.

#6VReality  Members

Posted 02 November 2011 - 11:50 AM

This subsystem holds a reference to each component that was created that it manages in an internal vector and when your game loop iterates, you iterate like components together. This also allows you to perform updates on your components in an ordered fashion, regardless of how they may have been inserted into your entities component vector during creation time.

Not to mention, instead of references, the subsytems could manage the actual component memory, making the processing of like components cache friendly with regard to data as well as code.

But taking your concept one step further, since the sub-systems own the components, anyone who needs access to them (including other components of the same object) could theoretically get it from the sub-systems. So technically, the entity object (with its member vector) doesn't even need to exist. It could, for instance be represented by a unique entity ID, and made up of all the various components filed with the sub-systems under that ID.

And this would remove the need for inheritance in the components (BaseComponent). (As your example is written, there's already no need for BaseSystem, but I could see that changing in a real implementation.)

#7GorbGorb  Members

Posted 02 November 2011 - 12:17 PM

I don't want to comment the idea of having an entity object as I don't claim to have found the Holy Grail, but I think it's unclean to use strings to identify components, especially if all component types are known at compile time. Instead, use type erasure:


class entity
{
private:
class base_entity_wrapper
{
public:
std::type_info *id;
virtual ~base_entity_wrapper() {}
};
template< class Type >
class entity_wrapper
: public base_entity_wrapper
{
public:
entity_wrapper( Type object )
: obj( std::move( object ) ) ,
id( &typeid( Type ) ) {}
Type obj;
};
std::vector< base_entity_wrapper* > components;
public:
template< class Type >
{
components.push_back( new entity_wrapper< Type >( std::move( object ) ) ); //currently not exception safe, use std::unique_ptr
}
template< class Type >
Type *get()
{
for( auto it = components.begin() ; it != components.end() ; ++it )
{
if( *(**it).id == typeid( Type ) )
return static_cast< component_wrapper< Type >* >( *it )->obj;
}
return 0;
}
};

//using:
e.get< CA >();


You can increase performance drastically by using a handcrafted typeid, but I'm not sure whether posting one is worth the time.

#8jaeg  Members

Posted 02 November 2011 - 12:46 PM

I like that idea VReality but how would I get them to coordinate their actions on the appropriate node? Would I make the node a component like I would have to the entity's vitals like health and other stats?

#9Naros  Members

Posted 02 November 2011 - 02:04 PM

I like that idea VReality but how would I get them to coordinate their actions on the appropriate node? Would I make the node a component like I would have to the entity's vitals like health and other stats?

You could take your entity's vital stats and break them out into their own components such as Health, Armor, Power or other components and then have each of those components managed by a their respective system. From a modular point of view it makes sense and keeps you from interweaving the code that manages an entity's health from their power, etc. Depending upon the number of vitals you give your entities, this could mean that an entity could have 10-20 components just for each vital stat. I haven't reached this level in my game yet; however, while I want to keep things modular and separated, I also do not want to overload the component-entity model with lots of small chunks that could easily be packaged into a single component. For example, a VitalsComponent and a VitalsSubsystem. Would be curious if others feel separate systems for each vital stat is the better design approach or a hybrid combination approach eases the game loop iterations.

As far as how they interact, this is still an area where I am trying to find a solution that I genuinely like. One option I have seen developers use is to keep pointers to the components in the entity itself and then use a method in the entity to retrieve components by type, such as:

template<class T> T* getComponentByType(std::string& sComponentType) { ... }


Some argue that some components have a tight relationship, such as your render component and your transformation component. Your rendered mesh can be placed on the scene if it doesn't know where to place the node, right? But maybe in this case, the render component is just dormant and doesn't render anything until a transformation is added? You could still implement this model using the above, but you'd need to naturally check for a NULL return pointer and react accordingly.

Another alternative is to use something like a message/callback system. I have seen where some developers prefer using a signal/slot concept where your component has event handlers you add as a part of that unique component's purpose. Then when your component is initialized, it registers those callback methods with a dispatcher and when events it is interested in are detected, those functions are invoked. This same concept could then be used for query methods as well so that the render component can invoke a signal and the transformation component acknowledges the signal by placing the position vector in the event return pointer and end. The render component could read the return value and either cache the vector or simply use it if performance is not a critical concern.

Another solution which is a bit more traditional is to follow the pattern where you have an interface defined for your event callbacks such as:

virtual void handleEvent(const CGameEvent& evt) { ... }


The downside to have a single callback point is that now you have to use a switch/if check to differ between the various notifications you may receive from the message subsystem. This is where the signal/slot concept is a bit more robust.

What have others done?

#10VReality  Members

Posted 02 November 2011 - 03:06 PM

...how would I get them to coordinate their actions on the appropriate node? Would I make the node a component like I would have to the entity's vitals like health and other stats?

You could take your entity's vital stats and break them out into their own components such as Health, Armor, Power or other components ... however, while I want to keep things modular and separated, I also do not want to overload the component-entity model with lots of small chunks that could easily be packaged into a single component. For example, a VitalsComponent and a VitalsSubsystem.

The bottom line is that they definitely belong in a component. Not every game entity should have health. Not every game entity should have armor. Etc. Hence, that data and the functions which act on it should be componentized. If (near enough) everything that has health also has armor, then it probably makes sense for those stats to be in the same component.

There will be certain things which multiple components will be interested in, and those will need to be factored out. For instance, physics, locomotion, and AI, will all be interested in entity position. So position should be its own component, which might be managed by some sort of "spacial database" system.

Since all those other components depend on entity position, rather than each of them making the sub-system look it up every frame, you might opt to have them each cache a reference to it. In fact it's probably a good idea for each of them to verify its existence when they're created, since they can't work without it.

None of this conflicts with the idea of various components participating in message passing/signaling schemes, as they see fit.

And when it came to retrieving (or checking for) a component, rather than something like,

MyEntity.GetComponent(POSITION)

// or

MyEntity.GetPosition()

you'd use,

SpatialDatabase.GetEntityPosition(MyEntityID)

// along with stuff like

SpatialDatabase.GetEntitiesWithin(Location, Range)

#11jaeg  Members

Posted 02 November 2011 - 03:36 PM

I like the spacial database idea a lot. I can see it being helpful for culling and deactivation purposes as well. A majority of my entities won't really need a lot of vitals. The enemies really only need health since their speed and attack will probably be handled via the AI component's external scripting.

If this method works out like I think it will it'll be a lot cleaner than my current system which has separate managers for enemies, props, etc. I'm trying to work on my ability at OOP design and thinking while not overdoing it. I don't like inheriting to get entity types.

Now when having my other components communicating with the SpatialDatabase should I do so by events, make it a singleton, or pass it by reference to the individual systems?

#12VReality  Members

Posted 02 November 2011 - 03:55 PM

I'm trying to work on my ability at OOP design and thinking while not overdoing it.

Actually, the whole Entity-Component paradigm is a move away from object oriented, to "data oriented" (although that name doesn't exactly capture what it is we're breaking out into components, or why). But the point is that there is too rich a set of possible object types and features to conveniently characterize them all as specializations of each other, or to share features accurately via inheritance. So the object oriented approach breaks down.

In other words, organizing your design around using programming language objects to represent game world objects doesn't work. So we're organizing our design around using programming language objects to implement game world object features instead.

(Which doesn't necessarily eliminate the use of programing language objects to represent game world objects. It just allows for the possibility. But when it doesn't, those resulting objects end up looking and working quite different due to the design approach.)

#13Naros  Members

Posted 02 November 2011 - 04:18 PM

Since all those other components depend on entity position, rather than each of them making the sub-system look it up every frame, you might opt to have them each cache a reference to it. In fact it's probably a good idea for each of them to verify its existence when they're created, since they can't work without it.

Following the premise that we use a caching mechanic, when entity 123 is created from it's template, the template dictates that two components are necessary; position & render. The entity system will invoke the two subsystems for position and render to create their respective components for entity 123. But if the position component is listed after the render component in the load process; how then do you handle your render component getting the position from the spacial subsystem? Is this handled by having an interface defined for each component so that as you create each component for an entity, you store each component in a temporary list, then iterate each of them calling some virtual method such as OnInitialize() after each component has been allocated so they sync up prior to returning a success create for this entity?

How would you handle the notion of removing a component attached to an EntityID that another component depends upon and already has a reference to? I don't want that render component to continue holding that position reference (although bad example) and then that position component be reused for another entity latter and now all my pointers are being mix-matched.

#14VReality  Members

Posted 02 November 2011 - 04:38 PM

But if the position component is listed after the render component in the load process; how then do you handle your render component getting the position from the spacial subsystem?

If order matters, then you'll probably have to get it right. If the render component expects a position component to exist, then the tool must generate a position component before a render component.

If you don't want it to matter, there are many options. For example, you could implement some sort of lazy handle. So the render component would have a handle to the position component, and when it was used, it would look up the position component if it hadn't already. Or you could just not cache it at all, and look it up every time. In either case, rather than verifying at creation, the system blows up if the position component is missing the first time the render component tries to get it.

How would you handle the notion of removing a component attached to an EntityID that another component depends upon and already has a reference to?

It should be noted that just because component systems don't result in compile time entity definitions and entities are built at run time, doesn't mean that they ought to be used to implement dynamically morphing entity types. That's not really the main point of using a component based design. In other words, a component based entity system doesn't necessarily need to support arbitrary removal of a sub-set of an entity's components.

Dynamic structure building (e.g., creation of a squad object which controls a group of units, etc.) can be done on another level, designed to manage entity relations. Similarly sub-component structure building, (e.g., AI sub-objects which create and manage each other), might best be handled with a system specific to that. And again, an attachment system, for use with animation or physics, should probably also be implemented separately.

But if you were to support dynamic component changing, obviously removal of a requisite component would still be an error. So the simplest answer is, don't cache references to components that will be removed (although everything still blows up if the component is still missing next time you try to use it). If that's too ad hoc for you, then you need to develop a system of component relations, probably using some sort of smart handles.

Also, if the make-up of an entity can change over time, then you'll probably want an entity object to track that, to facilitate destruction of all the correct component objects at entity expiration (unless its acceptable to attempt component removal on all engine sub-systems).

#15smr  Members

Posted 02 November 2011 - 04:48 PM

Just a word of caution: Don't get too crazy with creating a component for every little thing. You will end up with an application so complicated and with so many little pieces it will become impossible to manage. Creating entities will be tedious because you'll be adding six components when one Stats component would do. Remember, some of the primary reasons to avoid inheritance is to reduce complexity and improve manageability. Having to select from 200 different components just to create some trash enemy will be the complete opposite of simple and easy to manage.

#16Naros  Members

Posted 02 November 2011 - 05:21 PM

How would you handle the notion of removing a component attached to an EntityID that another component depends upon and already has a reference to?

It should be noted that just because component systems don't result in compile time entity definitions and entities are built at run time, doesn't mean that they ought to be used to implement dynamically morphing entity types. That's not really the main point of using a component based design. In other words, a component based entity system doesn't necessarily need to support removal of components.

So when a mob is killed in the game, there are some a small number of components that may drive the entity's behavior at that point. If the mob had an AI component to control certain aspects of it's behavior, upon death that AI component does nothing. So this AI component just remains in the AI subsystem's component vector list until the entity is removed by the corpse subsystem?

#17Naros  Members

Posted 02 November 2011 - 05:35 PM

Just a word of caution: Don't get too crazy with creating a component for every little thing. You will end up with an application so complicated and with so many little pieces it will become impossible to manage. Creating entities will be tedious because you'll be adding six components when one Stats component would do. Remember, some of the primary reasons to avoid inheritance is to reduce complexity and improve manageability. Having to select from 200 different components just to create some trash enemy will be the complete opposite of simple and easy to manage.

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

Just because I give the designer 200 components doesn't mean they need to select those 200 each time they want to add an entity to the scene. I would imagine the game editor tool should allow the designer to pick and choose from those 200 components and build pre-fab entity templates that can also be inheritance based. This way if they change a combination at a parent level of the entity template hierarchy, all child templates would reflect the change. Is this not a common approach for game editoring/authoring tools?

#18VReality  Members

Posted 02 November 2011 - 05:36 PM

So when a mob is killed in the game, there are a small number of components that may drive the entity's behavior at that point. If the mob had an AI component to control certain aspects of it's behavior, upon death that AI component does nothing. So this AI component just remains in the AI subsystem's component vector list until the entity is removed by the corpse subsystem?

That seems reasonable. The AI could know how to act when the entity is dead (and that could even involve changing the internal makeup of the AI component, dynamically). Another option might be to replace the entire entity with a corpse which has the components it needs.

#19jaeg  Members

Posted 02 November 2011 - 05:49 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.

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.

#20Naros  Members

Posted 02 November 2011 - 06:01 PM

So when a mob is killed in the game, there are a small number of components that may drive the entity's behavior at that point. If the mob had an AI component to control certain aspects of it's behavior, upon death that AI component does nothing. So this AI component just remains in the AI subsystem's component vector list until the entity is removed by the corpse subsystem?

That seems reasonable. The AI could know how to act when the entity is dead (and that could even involve changing the internal makeup of the AI component, dynamically). Another option might be to replace the entire entity with a corpse which has the components it needs.

That is a thought, but then I question it's viability. Your corpse system would need to be notified upon death of an entity, easily triggered by the vitals system. The corpse system could query each of the subsystems that are necessary to create a "corpse" and get that entity's component's and create a copy of them but under the new entity id. Afterward the old entity's components could be removed and added to the object pool, allowing the render phase to display the corpse and conserve resources for game loop iterations too.

I am still stumped on the life-cycle process however...

At the beginning, an entity needs to be created and so the entity subsystem inspects the template for that entity and determines the components needed. Since components and subsystems are registered in a factory with static create methods, the entity manager can easily create those objects by calling the appropriate static functions and passing the entity id. Those subsystems will create their various components and then after that process, a "sync" step occurs so that all those components created can get references to one another. But when it comes time to remove an entity id from the entity system, 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?

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.