我正在编写一个实体组件系统,我遇到了一个常见问题:组件相互依赖。我将使用一个例子:
假设您制作了一个项目组件,其中包含项目共有的数据(例如,重量)。但是,如果您想制作设备组件怎么办?任何设备也必须是一个项目,所以你有两个依赖组件。这种is-a关系似乎很适合继承,所以我做了一个继承组件。
问题是,我现在需要两个“多态层”,而我编写的枚举标记系统不再工作了。此外,我可能希望稍后将该设备组件分支为更多类型。
#include <memory>
#include <map>
enum COMPONENT_TAG {
COMP_BASE,
COMP_ITEM,
COMP_ITEM_EQUIP
};
struct VirtualBaseComponent { // component
COMPONENT_TAG tag;
virtual ~VirtualBaseComponent() = 0;
};
VirtualBaseComponent::~VirtualBaseComponent() {};
struct ItemComponent : public VirtualBaseComponent {
COMPONENT_TAG tag = COMP_ITEM;
~ItemComponent() {};
};
struct EquipmentComponent : public ItemComponent {
COMPONENT_TAG tag = COMP_ITEM_EQUIP;
~EquipmentComponent() {};
};
struct Entity {
std::map<COMPONENT_TAG, std::unique_ptr<VirtualBaseComponent>> components;
template <typename T>
T* get_component(COMPONENT_TAG tag) {
auto found = components.find(tag);
VirtualBaseComponent* baseptr;
if (found == components.end()) {
baseptr = nullptr;
} else {
baseptr = found->second.get();
}
return dynamic_cast<T*>(baseptr);
}
};
int main() {
{
//set up a random item (a box)
Entity box;
box.components.insert(std::make_pair(COMP_ITEM, std::make_unique<ItemComponent>()));
//fetching a base component: ok, this works fine
ItemComponent *boxptr = box.get_component<ItemComponent>(COMP_ITEM);
//do_stuff(boxptr->item_property);
}
{// set up a sword
Entity sword;
sword.components.insert(std::make_pair(COMP_ITEM_EQUIP, std::make_unique<EquipmentComponent>()));
//but here, if I actually want to use some "item property" of the sword not related to its equipment-ness...
//oops! that retrieves a nullptr, because sword does not have a base item tag
ItemComponent *swordptr = sword.get_component<ItemComponent>(COMP_ITEM);
ItemComponent a = *swordptr; //dereferencing it crashes the program
}
}
那么,如何以多态方式设置和获取这些类型的组件呢?我想我可以在 get_component 方法中对标签进行 if 检查的大列表,但乍一看,这对我来说似乎是一个愚蠢的解决方案。