我正在写一个射击游戏(例如1942年,经典的2D图形),我想使用基于组件的方法。到目前为止,我考虑了以下设计:
每个游戏元素(飞艇,弹丸,加电,敌人)都是一个实体
每个实体都是一组可以在运行时添加或删除的组件。例如位置,雪碧,健康,IA,损坏,边界框等。
这个想法是飞艇,弹丸,敌人,威能不是游戏类。实体仅由其拥有的组件定义(并且可以随时间变化)。因此,玩家飞艇从Sprite,Position,Health和Input组件开始。上电有Sprite,Position,BoundingBox。等等。
主循环管理游戏“物理”,即组件之间如何交互:
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());
...
组件在主C ++应用程序中进行了硬编码。实体可以在XML文件中定义(lua或python文件中的IA部分)。
主循环对实体并不太在意:它仅管理组件。该软件设计应允许:
给定一个组件,获取它所属的实体
给定一个实体,获取类型为“ type”的组件
对于所有实体,做点什么
对于所有实体的组件,执行某些操作(例如:序列化)
我在考虑以下问题:
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;
通过这种设计,我可以获得#1,#2,#3(感谢boost :: fusion :: map算法)和#4。同样,一切都是O(1)(好的,不完全是,但是仍然非常快)。
还有一个更“常见”的方法:
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
...
};
另一个方法是摆脱Entity类:每种Component类型都位于其自己的列表中。因此,有一个Sprite列表,一个Health列表,一个Damage列表等。我知道由于实体ID,它们属于同一逻辑实体。这比较简单,但速度较慢:IA组件基本上需要访问所有其他实体的组件,这将需要在每个步骤中搜索其他组件的列表。
您认为哪种方法更好?boost :: fusion贴图适合以这种方式使用吗?