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.
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?
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.
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?
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.
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?
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:
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:
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.