假设我有两种类型:实体和组件。
class Entity {
public:
entity_id id;
std::vector<component_id> components;
};
和
template<typename UnderlyingComponentType>
class Component {
public:
static std::vector<UnderlyingComponentType> components;
component_id id;
entity_id containingEntity;
}
在一个简单的例子中,每个实体包含多个不同类型的组件(例如TextComponent,SoundComponent,WeightComponent)。但是,不是将这些组件直接包含在字段中,而只是将它们的ID存储在实体中。这允许对相同类型的组件进行快速迭代,因为它们都在一个连续的向量中(例如,可以一次遍历所有的DescriptionComponents)。同样,可以为任何给定的实体检索给定类型的任何组件:
DescriptionComponent& decription = myEntity.getComponent<DescriptionComponent>()
// Can go to the DescriptionComponent's static vector, and look up the
// appropriate component given our entity's ID (matching it with the component's
// containingEntity ID.)
可以提供类似的功能来删除单个组件或添加。但是,当我想删除给定实体的所有组件时,我遇到了问题。因为组件存储在实体之外,所以没有一种简单的方法可以说“遍历所包含的组件并将其全部删除”。相反,我们必须为我们包含的每个组件类型转到底层静态向量,并从那里删除。这是我碰到砖墙的地方。这里似乎有两种可能的解决方案:
1。)在每个实体中存储一组类型,这些类型映射到我们包含的所有组件类型的组件向量(例如entity.getComponentTypes == X<DescriptionComponent, SoundComponent>;
这里的缺点是每个实体的组件可以不断变化(我可以在上面的例子中,在运行时添加一个WeightComponent
。因此,我不相信有一种方法可以根据运行时类型信息链接到编译时类型(如type_index
es或者等)
2。)生成实现Component的每种类型的编译时列表,然后当Entity要求删除其所有组件时,遍历它们 all 。例如,假设我们有一个实体e
,其标识为123
,并包含DescriptionComponent
和SoundComponent
。我们仍然会有一个包含[DescriptionComponent, SoundComponent, WeightComponent]
的类型的编译时列表,我们会告诉每个Component的静态向量删除123
引用的任何组件。由于只有DescriptionComponent
和SoundComponent
向量具有这些引用,因此只有那两个会删除组件,并且e
的所有组件都将被删除。
上面的选项2似乎是更可行的选项:创建类型的编译时列表。手动编写代码很容易:std::tuple<DescriptionComponent, SoundComponent, WeightComponent>
可以轻松存储并允许我根据需要迭代这些类型。但是,我理想的是一种生成此列表的方法以编程方式,因此,如果我稍后进入并添加NameComponent
,我将不必更新任何其他代码:当我从模板Component
类继承时,它应该自动管理。例如,
class NameComponent : public Component<NameComponent> {
//implementation details
}
//Now, my all-types list should contain [DescriptionComponent, SoundComponent,
// WeightComponent, NameComponent], and I didn't have to update any config.
我完全不确定上述内容是否可行,但任何帮助或资源建议都是可行的。我在this StackOverflow question看过类似的问题,但它依赖于宏(据我所知,它不会以相同的方式与模板交互)。同样地,如果看起来这是一个A / B问题,并且像type_index
地图那样的简单解决方案实际上将工作,那么这种批评也会受到欢迎!我确信我忽略了一些细节或措辞不好,所以如果/何时需要更多信息,请随时告诉我。
答案 0 :(得分:1)
您可以使用类型擦除并存储知道要处理哪种Component类型的元素,而不是将component_id
存储在实体的向量中。
#include <memory>
struct component_id{};
struct component_type_ref
{
template <typename Comp>
component_type_ref()
: _id(Comp::id), _impl(std::make_shared<_impl_t<Comp>>())
{
}
component_id id() const
{
return _id;
}
void clear()
{
_impl->clear();
}
private:
struct _impl_base
{
virtual void clear();
};
template <typename Comp>
struct _impl_t : public _impl_base
{
void clear()
{
Comp::components.clear();
}
};
component_id _id;
std::shared_ptr<_impl_base> _impl;
};
另见https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil
这样,例如,您可以在实体的向量中查找组件ID,并在其上调用clear。因此,如果您想要在组件类型上运行已知的操作列表(并且这些操作不需要是模板),那么可能是一个解决方案。
如果不是这样,那么我恐怕你需要使用类型向量。这里不需要使用元组,简单
template<typename... T> struct my_type_vector{};
就足够了。但正如你自己写的那样,这在运行时并不那么可行。
答案 1 :(得分:0)
您可以使用以下内容制作与std::type_index
类似的内容:
template<typename T>
void type_id() {}
using type_id_t = void(*)();
现在在你的地图中,使用:
std::map<type_id_t, /* whatever */> myMap;
并像这样检索:
auto myElement = myMap[type_id</* somthing else */>];
这将在没有RTTI的情况下完成工作