Jump to content
  • Advertisement
Sign in to follow this  
Nevadaes

Component-based entity system questions

This topic is 2618 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
Advertisement
Having the component name in the retrieval method violates the Open Closed principle and is certainly not OOP.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
How about something like this?

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;
}

Share this post


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

[font="Courier New"]const int IComponent_<T>::IComponet_ID = (int)&IComponent_<T>::IComponet_ID;[/font]

to create IDs automatically.

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

Share this post


Link to post
Share on other sites
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?

Share this post


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

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites
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?
[/quote]
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());

Share this post


Link to post
Share on other sites
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>[color="#0000ff"][color="#0000ff"]static [color="#0000ff"][color="#0000ff"]const ComponentType sk_ComponentType;[color="#0000ff"][color="#0000ff"]virtual [color="#0000ff"][color="#0000ff"]const ComponentType& GetType() [color="#0000ff"][color="#0000ff"]const { [color="#0000ff"][color="#0000ff"]return sk_ComponentType; };</code>

[color="#0000ff"][color="#0000ff"]

const ComponentType HealthComponent::sk_ComponentType([color="#a31515"][color="#a31515"]"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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

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

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!