【问题标题】:Variable-size heterogeneous container with type-safe lookup by type按类型进行类型安全查找的可变大小异构容器
【发布时间】:2015-02-19 14:48:45
【问题描述】:

我想实现实体组件系统,其中每个实体都有一个组件列表。每个组件都派生自 BaseComponent 类。实体中的每个组件都是唯一的——例如,实体不可能有两个 TransformComponent 组件。我想使用 getComponent() 方法从实体快速且类型安全地访问组件:

template <typename T>
T * getComponent();

在实体中实现组件容器的最佳方式是什么?

PS:我有很多实体(大约 10-2 万个),每个实体大约有 2-3 个组件,最多 10 个。所以我担心 unordered_map 对于我的任务来说太重了。

【问题讨论】:

  • unordered_map&lt;type_index, unique_ptr&lt;BaseComponent&gt;&gt;,我猜。
  • 为什么需要知道具体的子组件类型?为什么不能使用BaseComponent 中的(可能是抽象的)接口?
  • 我想过。但是我有很多实体(大约 10-20 千),每个实体大约有 2-3 个组件,最多 10 个。 unordered_map 对于这个任务来说是不是太重量级的解决方案了?
  • To Mark:我需要对实体进行大量转换,因为某些组件位于实体内部并编辑这些组件
  • 在宣布unordered_map 版本“太重量级”之前,您是否实际测量过它的性能?有序版本或排序向量也是可能的选项。

标签: c++ c++11


【解决方案1】:

我会建议使用元组方法作为稻草人来确定您的其他要求:

#include <utility>

template <typename...Components>
class Entity : private Components... {
public:
    Entity() = default;

    Entity(Components...components) :
        Components(std::move(components))... {}

    template <typename T>
    T& get() {
        return *this;
    }
    template <typename T>
    const T& get() const {
        return *this;
    }
};


#include <iostream>

struct ComponentA { void f() const { std::cout << "I'm a ComponentA\n"; } };
struct ComponentB { void f() const { std::cout << "I'm a ComponentB\n"; } };
struct ComponentC { void f() const { std::cout << "I'm a ComponentC\n"; } };

int main() {
    {
        Entity<ComponentA, ComponentB, ComponentC> e;
        e.get<ComponentC>().f();
        e.get<ComponentB>().f();
        e.get<ComponentA>().f();
    }

    {
        ComponentA a;
        Entity<ComponentA, ComponentB> e{a, {}};
    }

    {
        // error: duplicate base type
        // Entity<ComponentA, ComponentA> invalid_entity;
    }
}

它的优点是所有类型都是具体的,Component 类型甚至不需要相关。

【讨论】:

  • 元组是完美的,除了一件事——它是固定长度的容器,但我需要在运行时添加和删除组件
【解决方案2】:

我决定使用简单的方法在 Entity 中包含数组 *BaseComponent m_comps[MAX_COMPONENTS] 并为每个组件类型分配唯一的 ID。因为组件类型的数量限制在 20-30 个。这样,我将能够以最快的速度访问组件,只需在实体内存储 20-30 个指针的开销很小(并且它们将与实体本身一起分配在一个块中,从而允许良好的缓存局部性)

我使用此代码进行类型安全访问:

template <typename T>
T * Entity::getComp()
{
  return (T*)m_comps[T::idCompType];
}

并且每个组件都应该声明 const 静态成员 idCompType

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多