• Create Account

## Component-based entity system questions

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.

13 replies to this topic

137
Like
0Likes
Like

Posted 07 March 2011 - 06:15 PM

Hello there everyone,

I'm writing a small game while creating a component-based entity system. The game is coded in C++ and I'm using OOP to design it. I've read a lot on the matter and I think I came up with a viable solution.
There is an Entity class which has a vector of Component object. The Component class is only an interface class from which the other components inherits.

My first question comes here: I want to be able to retrieve a derived component (for example, the ComponentTransform which has functions such as setPosition()) and use its functions. So I wrote a little function in the Entity class which is as follow (most of the code is omitted):
class Entity
{
public:
Entity();
~Entity();

template <class T>
T* getComponent()
{
std::string name = typeid(T).name();

if (name.find("Transform") != std::string::npos)
return dynamic_cast<T*>(retrieveComponent("Transform"));

return NULL;
}

private:
std::vector<Component*>		m_Component;
Component* retrieveComponent(std::string name);
}; // Class


The "retrieveComponent" function only returns the Component in the vector which has the same name that is passed as an argument. Is it a viable solution? I mean, is it considered OOP to check the class name with "typeid", which in this case returns "class ComponentTransform" and to then cast a pointer of the type passed? I didn't wanted to come up with a return function which returns the component from its name, because that would be error-prone from the user, but as the name check is made inside the class definition, it is safer. Is it OOP? Or at least, is it a more viable solution that to use:
pEntity->getComponent("Transform")->foo();


Now, here's my second question, regarding the update process. Each components implements 5 functions. 3 of those are named "preUpdate()", "onUpdate()", "postUpdate" (I let you guess what they do). The way I'm updating the game right now is as follow:

I have two managers: an EntityManager, which holds a vector of every Entity and a ComponentManager, which holds a vector for every type of components, which means that I have:
std::vector<ComponentTransform*> m_ComponentTransform;
std::vector<ComponentModel*> m_ComponentModel;

I'll explain why in a few moments. Now, my EntityManager is called first, which calls an "update()" method from every Entity objects in the vector. This functions enables or disables its components as needed. Then, the ComponentManager "update()" method is also called. which calls the "xxUpdate()" method from the components in a specific order. For example, ComponentModel objects' "xxUpdate()" method is called before ComponentRender's. This is way, I can truly separate the code from which components: the ComponentModel sets the mesh to render, then the ComponentRender draws it on screen, etc.

What I wanted to know is: is it a good way of updating the game data? For now, it doesn't seem to slow anything nor to be inconvenient for the coder, but still, I was wondering if there was a better way of doing it.

Tell me if there is something that I didn't explained well. I would really like to have your comments on my way of doing this as well as suggestions you could make.

### #2return0  Members

508
Like
0Likes
Like

Posted 07 March 2011 - 06:44 PM

Having the component name in the retrieval method violates the Open Closed principle and is certainly not OOP.

137
Like
0Likes
Like

Posted 07 March 2011 - 06:56 PM

Yes, and I forgot to specify that this particular function is private in the Entity class. Is it still considered non-OOP? What would be my alternatives to retrieve a component without having to make a lot of dynamic_casting to check if the type is the correct one?

### #4Sudi  Members

761
Like
1Likes
Like

Posted 07 March 2011 - 07:43 PM

problem here of course is if the component doesn't exist in the entity. I actually solved this paradigm by using slots to call functions on classes that might be there.

#include <list>
#include <cstdio>
#include <cstdlib>
#include <string>

struct IComponent
{
virtual const int& getComponentTypeIdent(void) const = 0;
};

template<class T>
struct IComponent_ : IComponent
{
static bool check_ID(const IComponent* comp)
{
return comp->getComponentTypeIdent() == IComponet_ID;
}
static T* convertComponent(const IComponent* comp)
{
return check_ID(comp) ? (T*)comp : 0;
}
const int& getComponentTypeIdent(void) const
{
return IComponet_ID;
}
static const int IComponet_ID;
};
template<class T>
const int IComponent_<T>::IComponet_ID = (int)&IComponent_<T>::IComponet_ID;

struct IEntity
{
template<class T>
T* getComponent(void)
{
std::list<IComponent*>::iterator it = Components.begin();
while (it != Components.end())
{
if (IComponent_<T>::check_ID(*it))
{
return IComponent_<T>::convertComponent(*it);
}
++it;
}
printf("Failed on retriving a component from an Entity\n");
return 0;
}
std::list<IComponent*> Components;
};

struct superComp : IComponent_<superComp>
{
void puh(void)
{
printf("superComp\n");
}
};

struct superComp1 : IComponent_<superComp1>
{
void puh1(void)
{
printf("superComp1\n");
}
};

struct superComp2 : IComponent_<superComp2>
{
void puh2(void)
{
printf("superComp2\n");
}
};
int main(int argc, char* argv[])
{
IEntity entity;
entity.Components.push_back(new superComp);
entity.Components.push_back(new superComp1);

entity.getComponent<superComp>()->puh();
return 0;
}


### #5aeroz  Members

171
Like
0Likes
Like

Posted 08 March 2011 - 02:57 PM

My implementation looks like this:

template <typename CompType>
CompType* Entity::getComponent()
{
return static_cast<CompType*>( getComponent(CompType::COMPONENT_ID) );
}

Component* Entity::getComponent(const CompIdType& compId )
{
ComponentMap::iterator i = m_components.find( compId );
if ( i == m_components.end() )
return NULL;
else
return i->second.get();
}


CompIdType is a std::string.
ComponentMap is a std::map
Every component must have a COMPONENT_ID static variable.

@Sudi:

I think my solution is similar to yours (appart from IComponent_). But it's interesting how you use the line

const int IComponent_<T>::IComponet_ID = (int)&IComponent_<T>::IComponet_ID;

to create IDs automatically.

What do the 'I's mean in front of the class name? Is that an Interface? If yes, why IEntity?

137
Like
0Likes
Like

Posted 08 March 2011 - 06:13 PM

Thank you guys for your answers as well as suggestion. So is it considered OOP to have a public enum in the Component class, then having a static const int variable defined by the values in the enum in each of the subclasses to check if the type of component is the good one?

Also, I would like to know if you have any input of my update procedure that I described in my first post?

### #7Scourage  Members

1103
Like
0Likes
Like

Posted 08 March 2011 - 08:43 PM

So I define my entities (which are component based) slightly differently. An entity is made up of attributes, or publicly readable values and behaviors or components. Behaviors may register an attribute with the entity if they need to make some value available to other behaviors (like health). Instead of reaching into a behavior and twiddling some bits, I send the entity messages. Entities only have onMessage() and onUpdate() functions. The entity then passes the message on to each of the behaviors that care about that message type. If I think that there is a performance benefit to back-dooring the system and directly connecting components to each other, thats a possibly, but it rarely happens.

The update phase is done in several phases and with buckets. The entity manager has a hash map of all the entities in the system (and everything is an entity). There are several "views" of the database. There's a spatial view (octree based), an AI view, a remote entity network view, a player proximity view, a left-handed girlscout view...well you get the point. There is also an "update" view that has all the entities that need updating in each frame. They're stored in buckets depending on their dependencies. This means that he guy on a horse, on an aircraft carrier, on the ocean are all updated in the right order so that parent movement are translated down the hierarchy properly. The entities are also updated in a phased update. There are physics, animation and state updates. If I need to add more phases, that's trivial. The final phase is the attribute update phase. Attributes are double buffered so that entities are easier to update in a multi-threaded environment. The final phase copies the updated values over to the public value, which is read only during the other update phases.

Cheers,

Bob

Halfway down the trail to Hell...

### #8wild_pointer  Members

291
Like
2Likes
Like

Posted 09 March 2011 - 12:52 AM

Thank you guys for your answers as well as suggestion. So is it considered OOP to have a public enum in the Component class, then having a static const int variable defined by the values in the enum in each of the subclasses to check if the type of component is the good one?

Also, I would like to know if you have any input of my update procedure that I described in my first post?

My first suggestion would be to stop making "is it OOP" your first consideration when selecting a solution to a problem. The term is misunderstood to the point of being almost meaningless anyways. A better question might be to ask if given solution is it as simple as possible while satisfying all requirements and being easily maintainable. In which case I'd say an enum is probably fine.

As for your updating of components the only things that jumps out at me is first what exactly your entity manager has to do? Why do components need to be enabled or disabled? And second, why are you storing pointers to components in both the entity and the component manager? Does the entity really need to be able to query it's components directly? Could you instead store a mapping from components to their owning entity in your component system and direct all queries through that? And at that point do you even really need an Entity class? Could your entities just be a unique id?

### #9aeroz  Members

171
Like
0Likes
Like

Posted 09 March 2011 - 01:52 AM

As for your updating of components the only things that jumps out at me is first what exactly your entity manager has to do? Why do components need to be enabled or disabled? And second, why are you storing pointers to components in both the entity and the component manager? Does the entity really need to be able to query it's components directly? Could you instead store a mapping from components to their owning entity in your component system and direct all queries through that? And at that point do you even really need an Entity class? Could your entities just be a unique id?

I think that removing the Entity class is a good idea. I'm planning on doing that too in may game, because the code doesn't depend on the Entity class by itself. In components, I have code like:

getOwnerEntity()->getComponent<CompPhysics>();

This would be replaced by

componentManager->getComponentInEntity<CompPhysics>(getOwnerEntityId());

### #10Nanoha  Members

2437
Like
0Likes
Like

Posted 09 March 2011 - 02:15 AM

The problem with having an enum component type is that when you want to add a new type you have to add it to that enum. I use a hashed string instead

<code>staticconst ComponentType sk_ComponentType;virtualconst ComponentType& GetType() const { return sk_ComponentType; };</code>

const
ComponentType HealthComponent::sk_ComponentType("health_component");

I kind of need to do it this way as the base class is in a lib so I can't really add enums. Using int identifiers would be ok too. When I want to get a component I do:

ComponentPtr component = entity.GetComponentByType(HealthComponent::sk_ComponentType);

I still have to cast it to an appropriate type afterwards. I like the previous posters idea of using a template get function.

May I make a suggestion, along with your 3 update methods I have OnJoin and OnExit (when a component is added to or removed from an entity) which passes in a pointer to the parent (entity). Sometimes components are dependand on each other and this gives yuo an opportunity to wire them up.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

### #11JTippetts  Moderators

12316
Like
4Likes
Like

Posted 10 March 2011 - 07:27 PM

I handle my own components quite differently, limiting communication with an object to the passing of messages or requests, and never asking for direct access to a component for any reason. In my system, you don't really care whether an object has a HealthComponent or not; you just pass it messages along the lines of ModifyHealth, and let any interested components handle the message as they see fit. If no component wants to handle the message, then it is silently ignored. And it is possible for multiple components to handle the same message.

The only means of working with an entity that I provide are handleMessage() and handleRequest(); there is no way to get access directly to any component.

So as an example, Entity A makes a damaging attack against Entity B. Instead of querying Entity B for its HealthComponent and modifying it directly, I instead calculate the damage numbers and pass them as arguments in a "ModifyLife" message sent to Entity B. Entity B has a HealthComponent that registers to listen for "ModifyLife" messages, and when the message is received it will handle applying the damage accordingly. Now, since Entity B is a non-aggressive monster, its MonsterController component also registers to listen for "ModifyLife", and upon receipt of a "ModifyLife" message, it will signal to enter a defensive combat state. Exactly what this defensive state is, of course, depends on the exact implementation of the MonsterController. Some will respond with fight, some will respond with flight, etc... Also, since Entity B is a selectable enemy that the player can target, it also has a third component which listens for "ModifyLife" messages: a CharacterNameplateDisplay component, which will modify the on-screen lifebar representation to reflect the decrease in life.

Now, what if I want to make Entity B into a sort of booby-trap exploding decoy? Instead of adding a HealthComponent, CharacterNameplateDisplay component, etc... instead I add a ExplodesOnHit component. Now, without any change to the sequence of actions required on Entity A's part (I still generate an attack, roll damage numbers, and pass a "ModifyLife" message as before) I have completely changed the way Entity B responds to an attack. The ExplodesOnHit component registers to listen for "ModifyLife", only instead of performing life calculations, it just triggers an exploding response, querying the environment for all objects in a radius, calculating a damage value and delivering this damage value as "ModifyLife" messages sent to all objects in the radius, then triggering a special effect ("Explode") and finally triggering DestroySelf message to despawn the trapped object.

In this way, I decouple the action (making an attack) from the response to the action (modifying health level, modifying lifebar display, exploding), and not once do I ever care exactly what component or type of component is needed to handle the message.

I wrote a bit more detailed explanation in my journal here.

### #12Doggolainen  Members

115
Like
0Likes
Like

Posted 16 March 2011 - 05:23 PM

I handle my own components quite differently, limiting communication with an object to the passing of messages or requests, and never asking for direct access to a component for any reason. In my system, you don't really care whether an object has a HealthComponent or not; you just pass it messages along the lines of ModifyHealth, and let any interested components handle the message as they see fit. If no component wants to handle the message, then it is silently ignored. And it is possible for multiple components to handle the same message.

The only means of working with an entity that I provide are handleMessage() and handleRequest(); there is no way to get access directly to any component.

So as an example, Entity A makes a damaging attack against Entity B. Instead of querying Entity B for its HealthComponent and modifying it directly, I instead calculate the damage numbers and pass them as arguments in a "ModifyLife" message sent to Entity B. Entity B has a HealthComponent that registers to listen for "ModifyLife" messages, and when the message is received it will handle applying the damage accordingly. Now, since Entity B is a non-aggressive monster, its MonsterController component also registers to listen for "ModifyLife", and upon receipt of a "ModifyLife" message, it will signal to enter a defensive combat state. Exactly what this defensive state is, of course, depends on the exact implementation of the MonsterController. Some will respond with fight, some will respond with flight, etc... Also, since Entity B is a selectable enemy that the player can target, it also has a third component which listens for "ModifyLife" messages: a CharacterNameplateDisplay component, which will modify the on-screen lifebar representation to reflect the decrease in life.

Now, what if I want to make Entity B into a sort of booby-trap exploding decoy? Instead of adding a HealthComponent, CharacterNameplateDisplay component, etc... instead I add a ExplodesOnHit component. Now, without any change to the sequence of actions required on Entity A's part (I still generate an attack, roll damage numbers, and pass a "ModifyLife" message as before) I have completely changed the way Entity B responds to an attack. The ExplodesOnHit component registers to listen for "ModifyLife", only instead of performing life calculations, it just triggers an exploding response, querying the environment for all objects in a radius, calculating a damage value and delivering this damage value as "ModifyLife" messages sent to all objects in the radius, then triggering a special effect ("Explode") and finally triggering DestroySelf message to despawn the trapped object.

In this way, I decouple the action (making an attack) from the response to the action (modifying health level, modifying lifebar display, exploding), and not once do I ever care exactly what component or type of component is needed to handle the message.

I wrote a bit more detailed explanation in my journal here.

I've been into the idea of message passing between entities in my game framework. But I always get stuck on on how to implement the argument passing correctly for the messages. I always end up with ugly typecasts on the other end (the entity that receives the message).

In your case, I assume you pass some kind of float value along with the "ModifyLife" message. Could you please tell me how you would implement the receiving part of that message?

### #13JTippetts  Moderators

12316
Like
0Likes
Like

Posted 17 March 2011 - 06:16 PM

I've been into the idea of message passing between entities in my game framework. But I always get stuck on on how to implement the argument passing correctly for the messages. I always end up with ugly typecasts on the other end (the entity that receives the message).

In your case, I assume you pass some kind of float value along with the "ModifyLife" message. Could you please tell me how you would implement the receiving part of that message?

Well, since all my high-level logic is handled in Lua rather than C++, it's not really an issue. I just populate a table and pass that as the arguments for the message.

### #14Doggolainen  Members

115
Like
0Likes
Like

Posted 18 April 2011 - 02:34 PM

I've been into the idea of message passing between entities in my game framework. But I always get stuck on on how to implement the argument passing correctly for the messages. I always end up with ugly typecasts on the other end (the entity that receives the message).

In your case, I assume you pass some kind of float value along with the "ModifyLife" message. Could you please tell me how you would implement the receiving part of that message?

Well, since all my high-level logic is handled in Lua rather than C++, it's not really an issue. I just populate a table and pass that as the arguments for the message.

Ive made some progress in my "engine" which I nowadays call a game. I've decided to make a few games and then take parts out of them to make an engine (thanks to this forum Ive realized it was a better approach for me).

Anyhow, I've got my self a world divided into tiles. Think of it as and old-school top-view game, where an entity only can move between tiles. I've got query functions in my gameworld such as "GetEntititiesAt(Coordinate position)" which would return all the entities stored in a tile at that position. But how am I suppose to handle return parameters using my message systems?

I'm thinking of implementing some kind of broadcast system. So that if a component needs to know of its surroundings, I let them subsrcibe on the "GetSurroundings" message. And then let the gameworld/someComponent fire that message each frame to let every subrscriber know of their surrounding. Either that or some kind of callback function, so that when an entity asks for its surroundings, they could pass a reference to a list or something which will be filled.

This is the first problem of this type Ive ran into so far, and I expect to see more of it, so I want to have a method of dealing with it asap.

My question in short terms: Do you have messages that also returns stuff?
If yes: How do you handle if several objects would subscribe on the same message?
If no: How do you do it?

//Doggolainen

Edit:
@JJTippets: A quote from your blog that pretty much hits spot on what this topic is about.
http://www.gamedev.net/blog/33/entry-2249433-how-goblinson-crusoe-works-the-tldr-version/

There is also a function,handleRequest(), which acts similarly tohandleMessage(), but which will return a list of results. This is used for queries, and I am unsure on whether or not it is actually needed, as I haven't really used it much.

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.