Recently I started working at a game framework. The idea is to create a reusable framework that can be used across multiple platforms and libraries (I.e. Irrlicht, OGRE, SDL). Myself, I already have some experience with game programming (Entwined Worlds). However, all my previous game implementations were based upon the traditional deep hierarchy approach. At first sight this seems like a good idea, but I noticed that this approach always leads to very specialized entities. The true problem with this is that those entities often share various functionalities. And in order to prevent rewriting the same code over and over again, these functionalities are often put in a derived class from the base entity class. And the specialized entities then inherit from that class. The problem should become clear now: for every set of shared functionalities the programmer has to inherit from the base class (or maybe even an derived class). This can result in a gigantic unmaintainable tree of entities. Luckily, there is another approach called Component Oriented Entities.
The idea behind Component Oriented Entities is to make entities data-driven. In order to do this the functionalities are split in various individual components. As a result an entity is in fact nothing more than a collection of components. An added advantage is the ability to describe these components (and thus the entities) in XML or a similar mark-up language.
Yesterday I actually had a discussion with Nick about Component Oriented Entities. Initially he disagreed with me as I had some trouble explaining the “dangers” of the blob anti-pattern (a huge single class with a large amount of complex functionality). Later on, he agreed with me, but he also pointed out some things to keep in mind. One of those things was the implementation. And I really agree with that as these kind of things stand or fall based upon the implementation. Which brings me to my own implementation of Component Oriented Entities. Please note that all of the following code has been placed in the public domain and that I’ll take no responsibility or liability of any kind for any use that you may make of this code.
Lets start with the Entity Manager.
EntityManager.h
#ifndef __ENTITYMANAGER_H__
#define __ENTITYMANAGER_H__
// Include files
#include "../dependencies.h"
#include "Entity.h"
// Forward declarations
class Entity;
// EntityManager class
class EntityManager
{
public:
// Initialisation and deinitialisation
EntityManager();
~EntityManager();
void init();
void clear();
// Methods
bool addEntity(Entity *entity);
Entity* createEntity(const std::string &name);
Entity* getEntity(const unsigned int id);
Entity* getEntity(const std::string &name);
void removeAll();
bool removeEntity(Entity *entity);
bool removeEntity(const unsigned int id);
bool removeEntity(const std::string &name);
private:
vector<Entity*> mEntities;
};
#endif
EntityManager.cpp
// Include files
#include "EntityManager.h"
// EntityManager class
// EntityManager constructor.
EntityManager::EntityManager()
{
init();
}
// EntityManager deconstructor.
EntityManager::~EntityManager()
{
clear();
}
// Initialises the Entity Manager.
void EntityManager::init()
{
}
// Clears the Entity Manager.
void EntityManager::clear()
{
// Clear entities.
removeAll();
}
// Adds an entity to the Entity Manager.
bool EntityManager::addEntity(Entity *entity)
{
// Did we get a valid pointer?
if(entity == NULL)
return false;
// Look if a entity with given name doesn't already exist.
if(getEntity(entity->getName()))
return false;
// Add the entity.
mEntities.push_back(entity);
return true;
}
// Creates an entity with the given name and adds it to the Entity Manager.
Entity* EntityManager::createEntity(const std::string &name)
{
// Look if a entity with given name doesn't already exist.
if(getEntity(name))
return NULL;
// Add the entity.
Entity *entity = new Entity(name);
mEntities.push_back(entity);
return entity;
}
// Gets the entity with the given ID.
Entity* EntityManager::getEntity(const unsigned int id)
{
for(unsigned int i = 0; i < mEntities.size(); i++)
{
if(mEntities[i]->getID() == id)
return mEntities[i];
}
return NULL;
}
// Gets the entity with the given name.
Entity* EntityManager::getEntity(const std::string &name)
{
for(unsigned int i = 0; i < mEntities.size(); i++)
{
if(mEntities[i]->getName() == name)
return mEntities[i];
}
return NULL;
}
// Removes all entities.
void EntityManager::removeAll()
{
for(unsigned int i = 0; i < mEntities.size(); i++)
delete mEntities[i];
mEntities.clear();
}
// Removes the given entity.
bool EntityManager::removeEntity(Entity *entity)
{
// Did we get a valid pointer?
if(entity == NULL)
return false;
// Try to remove the entity.
vector<Entity*>::iterator it;
for(it = mEntities.begin(); it < mEntities.end(); it++)
{
Entity *ent = *it;
if(ent == entity)
{
delete ent;
mEntities.erase(it);
return true;
}
}
// We couldn't find the entity and thus couldn't remove it.
return false;
}
// Removes the entity with the given ID.
bool EntityManager::removeEntity(const unsigned int id)
{
// Try to remove the entity.
vector<Entity*>::iterator it;
for(it = mEntities.begin(); it < mEntities.end(); it++)
{
Entity *ent = *it;
if(ent->getID() == id)
{
delete ent;
mEntities.erase(it);
return true;
}
}
// We couldn't find the entity and thus couldn't remove it.
return false;
}
// Removes the entity with the given name.
bool EntityManager::removeEntity(const std::string &name)
{
// Try to remove the entity.
vector<Entity*>::iterator it;
for(it = mEntities.begin(); it < mEntities.end(); it++)
{
Entity *ent = *it;
if(ent->getName() == name)
{
delete ent;
mEntities.erase(it);
return true;
}
}
// We couldn't find the entity and thus couldn't remove it.
return false;
}
// End of File
The Entity Manager takes care of organizing all entities. Through the Entity Manager it’s possible to create, add, get and remove entities. To this end the Entity Manager offers various methods.
Now lets look at the Entity class.
Entity.h
#ifndef __ENTITY_H__
#define __ENTITY_H__
// Include files
#include "../dependencies.h"
#include "EntityComponent.h"
// Forward declarations
class EntityComponent;
// Entity class
class Entity
{
public:
// Initialisation and deinitialisation
Entity(const std::string &name);
~Entity();
void init();
void clear();
// Methods
unsigned int getID() const;
const std::string& getName() const;
bool addComponent(EntityComponent* component);
EntityComponent* getComponent(const unsigned int id);
EntityComponent* getComponent(const std::string &name);
void removeAll();
bool removeComponent(EntityComponent *component);
bool removeComponent(const unsigned int id);
bool removeComponent(const std::string &name);
private:
// Static members
static unsigned int mIDCount;
// Normal members
unsigned int mID;
std::string mName;
vector<EntityComponent*> mComponents;
};
#endif
Entity.cpp
// Include files
#include "Entity.h"
// Entity class
// Static variables.
unsigned int Entity::mIDCount = 0;
// Entity constructor.
Entity::Entity(const std::string &name)
: mName(name)
{
mID = mIDCount++;
init();
}
// Entity deconstructor.
Entity::~Entity()
{
clear();
}
// Initialises the entity.
void Entity::init()
{
}
// Clears the entity.
void Entity::clear()
{
// Remove all components.
removeAll();
}
// Gets the ID of this entity.
unsigned int Entity::getID() const
{
return mID;
}
// Gets the name of this entity.
const std::string& Entity::getName() const
{
return mName;
}
// Adds a component to the entity.
bool Entity::addComponent(EntityComponent *component)
{
// Did we get a pointer to a component?
if(component == NULL)
return false;
// Check if a component of the given type doesn't already exist.
if(getComponent(component->getName()))
return false;
// Add new component.
mComponents.push_back(component);
return true;
}
// Gets the component with the given ID from this entity.
EntityComponent* Entity::getComponent(const unsigned int id)
{
for(unsigned int i = 0; i < mComponents.size(); i++)
{
if(mComponents[i]->getID() == id)
return mComponents[i];
}
return NULL;
}
// Gets the component with the given name from this entity.
EntityComponent* Entity::getComponent(const std::string &name)
{
for(unsigned int i = 0; i < mComponents.size(); i++)
{
if(mComponents[i]->getName() == name)
return mComponents[i];
}
return NULL;
}
// Removes all components.
void Entity::removeAll()
{
for(unsigned int i = 0; i < mComponents.size(); i++)
delete mComponents[i];
mComponents.clear();
}
// Removes the given component.
bool Entity::removeComponent(EntityComponent *component)
{
// Did we get a valid pointer?
if(component == NULL)
return false;
// Try to remove the entity.
vector<EntityComponent*>::iterator it;
for(it = mComponents.begin(); it < mComponents.end(); it++)
{
EntityComponent *comp = *it;
if(comp == component)
{
delete comp;
mComponents.erase(it);
return true;
}
}
// We couldn't find the entity and thus couldn't remove it.
return false;
}
// Removes the component with the given ID.
bool Entity::removeComponent(const unsigned int id)
{
// Try to remove the entity.
vector<EntityComponent*>::iterator it;
for(it = mComponents.begin(); it < mComponents.end(); it++)
{
EntityComponent *comp = *it;
if(comp->getID() == id)
{
delete comp;
mComponents.erase(it);
return true;
}
}
// We couldn't find the entity and thus couldn't remove it.
return false;
}
// Removes the component with the given name.
bool Entity::removeComponent(const std::string &name)
{
// Try to remove the entity.
vector<EntityComponent*>::iterator it;
for(it = mComponents.begin(); it < mComponents.end(); it++)
{
EntityComponent *comp = *it;
if(comp->getName() == name)
{
delete comp;
mComponents.erase(it);
return true;
}
}
// We couldn't find the entity and thus couldn't remove it.
return false;
}
// End of File
As you can see an entity is a collection of an ID, name and a vector of components. That means that an entity is largely defined by its components. These are in my implementation described by the EntityComponent class.
EntityComponent.h
#ifndef __ENTITYCOMPONENT_H__
#define __ENTITYCOMPONENT_H__
// Include files
#include "../dependencies.h"
#include "Entity.h"
// Forward declarations
class Entity;
// EntityComponent class
class EntityComponent
{
public:
// Initialisation and deinitialisation
EntityComponent(Entity *parent);
virtual ~EntityComponent();
// Methods
unsigned int getID() const;
const std::string& getName() const;
const Entity* getParent();
protected:
void setName(const std::string &name);
private:
// Static members
static unsigned int mIDCount;
// Normal members
Entity *pParent;
unsigned int mID;
std::string mName;
};
#endif
EntityComponent.cpp
// Include files
#include "EntityComponent.h"
// EntityComponent class
// Static variables.
unsigned int EntityComponent::mIDCount = 0;
// EntityComponent constructor.
EntityComponent::EntityComponent(Entity *parent)
: pParent(parent)
{
// Get new unique ID.
mID = mIDCount++;
// Check if we got a valid pointer.
if(parent == NULL)
delete this;
else parent->addComponent(this);
}
// EntityComponent deconstructor.
EntityComponent::~EntityComponent()
{
}
// Sets the name of the component.
void EntityComponent::setName(const std::string &name)
{
mName = name;
}
// Gets the ID of the component.
unsigned int EntityComponent::getID() const
{
return mID;
}
// Gets the name of the component.
const std::string& EntityComponent::getName() const
{
return mName;
}
// Gets the parent of this component.
const Entity* EntityComponent::getParent()
{
return pParent;
}
// End of File
And just as the Entity class, the EntityComponent is also fairly simple. You might be wondering, though, how these components communicate with each other and other parts of the framework. The truth is that I’ve build an Event Manager on top of this entity system that takes care of that. Unfortunately that Event Manager depends on a third-party library called Sigslot And I think that such thing is beyond the scope of this post. Partly, also because I’m still tweaking my Event Manager myself and don’t feel comfortable yet showing that.

Getting pretty late, but what the heck.
Multiple Virtual Inheritance seems a good way to go with C++ and still doing this, but I still have to inherit from more than 2 classes and with some more complex ways. I dislike the components stuff because you can’t really tell the difference anymore between has-a and is-a relationships, a fundamental part of OOP I reckon. But hey, maybe that’s not that important. Or maybe it’s as simple as having two vectors, one for is-a relationships and one for has-a relationships.
Bedtime! Finally.
I’m trying to decipher what you meant with a has-a and is-a relationship. If you mean that an entity and its components should recognize each other, that’s already possible. An entity can access it’s components through the vector or the method getComponent(…). And components can access their parent entity through the parent pointer. This enables code like this:
AnimatedComponent::onUpdate()
{
Entity *entity = pParent;
EntityComponent *comp = entity->getComponent("CameraComponent");
// ^ You might want to do a cast there to access the specific code of the component.
}
By the way, I saw your post at DaniWeb. Looked pretty interesting. You might want to cross-post that here.
Well, in OOP objects can be other objects (a circle is a shape) and objects can have other objects (shapes have vertices). An object is defined but what it has and can do. Attaching 4 wheels to an engine does not make it a car, unless the engine’s power can be transferred to the wheels in some way.
With components, the separation between data (4 wheels, engine) and function (transfer power) is lost. I don’t know if that’s bad good or does not matter, but composition is something quit different from inheritance in C++, and maybe for a good reason.
As of April 25, 2009 the above code has been updated. It’s recommended to update your code if you’re using the above code.