I'm writing a shooter (like 1942, classic 2D graphics) and I'd like to use a component based approch. So far I thought about the following design:
Each game element (airship, projectile, powerup, enemy) is an Entity
Each Entity is a set of components which can added or removed at run-time. Examples are Position, Sprite, Health, IA, Damage, BoundingBox etc.
The idea is that Airship, Projectile, Enemy, Powerup are NOT game classes. An entity is only defined by the components it owns (and that can change during time). So the player Airship starts with Sprite, Position, Health and Input components. A powerup has the Sprite, Position, BoundingBox. And so on.
The main loop manages the game "physics", i.e. how the components interact each other:
foreach(entity (let it be entity1) with a Damage component)
foreach(entity (let it be entity2) with a Health component)
if(the entity1.BoundingBox collides with entity2.BoundingBox)
{
entity2.Health.decrease(entity1.Damage.amount());
}
foreach(entity with a IA component)
entity.IA.update();
foreach(entity with a Sprite component)
draw(entity.Sprite.surface());
...
Components are hardcoded in the main C++ application. Entities can be defined in an XML file (the IA part in a lua or python file).
The main loop doesn't care a lot about entities: it only manages components. The software design should allow to:
Given a component, get the entity it belongs to
Given an entity, get the component of type "type"
For all entities, do something
For all entity's component, do something (e.g: serialize)
I was thinking about the following:
class Entity;
class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
class Sprite : public Component {...};
class Position : public Component {...};
class IA : public Component {... virtual void update() = 0; };
// I don't remember exactly the boost::fusion map syntax right now, sorry.
class Entity
{
int id; // entity id
boost::fusion::map< pair<Sprite, Sprite*>, pair<Position, Position*> > components;
template <class C> bool has_component() { return components.at<C>() != 0; }
template <class C> C* get_component() { return components.at<C>(); }
template <class C> void add_component(C* c) { components.at<C>() = c; }
template <class C> void remove_component(C* c) { components.at<C>() = 0; }
void serialize(filestream, op) { /* Serialize all componets*/ }
...
};
std::list<Entity*> entity_list;
With this design I can get #1, #2, #3 (thanks to boost::fusion::map algorithms) and #4. Also everything is O(1) (ok, not exactly, but it's still very fast).
There is also a more "common" approch:
class Entity;
class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
class Sprite : public Component { static const int type_id = 0; };
class Position : public Component { static const int type_id = 1; };
class Entity
{
int id; // entity id
std::vector<Component*> components;
bool has_component() { return components[i] != 0; }
template <class C> C* get_component() { return dynamic_cast<C> components[C::id](); } // It's actually quite safe
...
};
Another approch is to get rid of the Entity class: each Component type lives in its own list. So there is a Sprite list, a Health list, a Damage list etc. I know they belong to the same logic entity because of the entity id. This is simpler, but slower: the IA components needs access basically to all other entity's components and that would require searching each other component's list at each step.
Which approch do you think is better? is boost::fusion map suited to be used in that way?