替代四重嵌套unordered_map怪物?

时间:2019-06-20 14:12:50

标签: c++ c++17 game-engine entity-component-system

我一直在尝试找到一种有效的方法来存储和检索许多对象。让我解释一下我要实现的目标,然后列出我想出的选择(但不满意)。

以下内容从技术上说是我需要做的,但是是明显的禁忌:

std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::unordered_map<uint32_t, Component*>>>>
//Scene -> Layer -> Type -> Id -> Component*         

最里面的地图根据组件的ID保存组件。每个类型前面都有一个映射(Component的子类)。这样做是为了使我在检索它们时,可以完全安全地将它们动态地转换为它们的类型,因为TYPE哈希图仅包含其类型的指针,还允许使用count来快速检查某个ID是否存在。 接下来的地图按层存储它们,第一个地图按场景存储它们。在任何时候,将保留大约30-50个场景,每个场景包含大约6-10层,每个场景包含大约30-40种类型,每种类型包含1到500个对象。

每个周期,我们将根据指针的类型遍历指针,一次迭代一层。场景变化很少(每2-3分钟一次)。可以使用类型和ID的组合来访问组件。代码会定期检查在同一ID中还存在哪些其他Component类型。 场景,图层和类型通过其名称进行访问,名称存储为32位CRC哈希。速度至关重要。 ID是由代码分配的数字,仅从0开始。每个场景中的ID都是唯一的。

毫无疑问,有一些疯狂的(惯用的)成语可以帮助我,但我从未听说过。 有人认识吗? 到目前为止,我没有提出任何备选方案,但是无论如何,我都会列出它们:

选项1:

std::unordered_map<uint32_t, std::vector<Component*>>
ID -> Component*

Component保留它来自哪个类型,场景和图层,每当迭代所有条目时,我们都会忽略不是来自当前场景或图层的条目。或者,按顺序存储它们,以便您只需要迭代一定范围。 向量包含组件,当我们需要访问某种类型的组件时,我们将在向量中进行搜索。这并不理想,因为它需要一个周期进行多次搜索。或者,使用unordered_map代替矢量。

选项2:

与嵌套地图相同,但带有矢量。映射将Id转换为向量内的索引。

选项3:

std::vector<Component*>
std::unordered_map<uint32_t, std::vector<int>>

(类型/图层/场景/ ID)->组件* 简单地使用向量索引存储所有分量。有一个unordered_map,它在主存储向量中包含索引向量。当我们检查两者之间的冲突时,ID和字符串哈希都可以存在(不太可能)。场景,图层和类型的名称必须唯一。 ID返回该ID的组成部分的所有索引的向量,Name或类型返回包含该类型或场景的所有索引的向量。这些向量的所有迭代都让人感到骇俗。

选项4:

Components获得一个“ Component * next”指针,以遍历属于同一实体的组件。最后一个组件链接到第一个。组件再次获得类型和场景/图层成员。

2 个答案:

答案 0 :(得分:6)

指定您自己的密钥,具有哈希功能和等于功能。

   class cKey
    {
    public:
      size_t scene;
      size_t layer;
      size_t type;
      size_t id;
    };

    unordered_map< cKey, Component*,
     hashkey, equalkey  >

一个人如何遍历一层这样的所有组件?

cKey key;
key.scene = S;
key.layer = L;

for( key.type = 0; key.type< LastType; key.type ++ ) {
for( key.id = 0; key.id < LastID; key.id++ ) {
   Component * pC = the_map.find( key ).second;
   ...

您可以在https://gist.github.com/JamesBremner/d71b158b32e4dd8ffaf8cbe93cf3f180找到实现 在250毫秒内对50,000个组件的映射中的一层进行迭代。

答案 1 :(得分:4)

我建议将地图分成多个地图:

std::unordered_map<std::uint32_t, std::vector<std::uint32_t>> layer_by_scene;
std::unordered_map<std::uint32_t, std::vector<std::uint32_t>> entity_by_layer;
std::unordered_map<uint32_t, std::vector<std::uint32_t>> component_by_entity;
std::unordered_map<uint32_t, Component*> components;

但是,请注意,在通常的Entity-Component-System中,您将尝试避免指针在基于节点的容器中追逐和跳来跳去。