我一直在尝试找出一种有效的方法来存储和检索许多对象。让我解释一下我要实现的目标,然后列出我想出的选择(但不满意)。
从技术上讲,以下内容可以满足我的需要,但显然是不行的:
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*
Run Code Online (Sandbox Code Playgroud)
最内部的映射根据组件的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*
Run Code Online (Sandbox Code Playgroud)
Component保留它来自哪个类型,场景和图层,每当我们遍历所有条目时,我们都会忽略那些不是来自当前场景或图层的条目。或者,按顺序存储它们,以便您只需要迭代一定范围。向量包含组件,当我们需要访问某种类型的组件时,我们将在向量中进行搜索。这并不理想,因为它需要一个周期进行多次搜索。或者,使用unordered_map代替矢量。
选项2:
与嵌套地图相同,但带有矢量。映射将Id转换为向量内的索引。
选项3:
std::vector<Component*>
std::unordered_map<uint32_t, std::vector<int>>
Run Code Online (Sandbox Code Playgroud)
(类型/层/场景/ ID)->分量*仅使用矢量索引存储所有分量。有一个unordered_map,它在主存储向量中包含索引向量。当我们检查两者之间的冲突时,ID和字符串哈希都可以存在(不太可能)。场景,图层和类型的名称必须唯一。ID返回该ID的组成部分的所有索引的向量,Name或类型返回包含该类型或场景的所有索引的向量。这些向量的所有这些迭代都让人感到骇俗。
选项4:
组件获取一个“ Component * next”指针,以遍历属于同一实体的组件。最后一个组件链接到第一个。组件再次获得类型和场景/图层成员。
我正在为我的游戏引擎实施 ECS 框架,并研究在运行时识别组件类型的方法。这意味着我可以在内存中连续动态地对类似类型的组件进行分组。例如,我可以有两个单独的数组Position和Velocity组件,我的系统可以循环遍历它们。
我目前使用typeid(),但我遇到了一篇使用static const变量为每种类型生成唯一 ID的文章。总体思路可以总结在下面的代码片段中:
#include <iostream>
struct Position { float x, y; };
struct Velocity { float dx, dy; };
template <typename T>
int test(int x) {
static const int y = x;
return y;
}
int main(int argc, char **argv) {
std::cout << test<Position>(1) << " ";
std::cout << test<Position>(2) << " ";
std::cout << test<Velocity>(3) << " ";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这个输出1 1 3,但我希望因为它试图修改一个常量(特别是在第二次调用时),它将无法编译。为什么这样做?
我想创建一个小的Entity-Component-System示例并创建一些组件,如
internal struct Position : IComponent
{
public int X { get; set; }
public int Y { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
和
internal struct MovementSpeed : IComponent
{
public int Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
每个组件实现一个当前为空的接口IComponent.当系统循环实体时,我想快速找到相关组件.
我想过创建一个Dictionary,它将组件类型作为键,将当前实体的组件作为值.
我开始了 public Dictionary<Type, IComponent> Components { get; }
我可以使用添加组件 myEntity.Components.Add(typeof(Movement), new Movement() as IComponent);
但是我怎样才能归还一个组件?我创建了一个运动系统示例
internal class Movement : ISystem
{
public void Update()
{
foreach (Entity entity in EntityPool.activeEntities.Values) // Loop through all entities
{
Dictionary<Type, IComponent> components …Run Code Online (Sandbox Code Playgroud) 此问题与在 Windows 10 操作系统中运行的 C++ 应用程序中 Visual Studio 2019 的编译器错误有关。这可能是一个中级或更高级别的问题。
我有这个功能:
template<typename T> T* Scene::GetComponent(EntityID entityID) {
CompTypeID typeID = TypeToID<T>();
if (HasComponent<T>(entityID)) {
CompIndex index = componentIndexes[typeID][entityID];
return components[typeID][index];
}
else return nullptr;
}
Run Code Online (Sandbox Code Playgroud)
此函数导致编译器错误 C2440:
'return':无法使用 [_Ty=Component*] 和 [T=Name] 从 '_Ty' 转换为 'T*'
此错误还附加了以下两条消息:
消息:从基类转换到派生类需要 dynamic_cast 或 static_cast
消息:请参阅对使用 [T=Name] 编译的函数模板实例化 'T *Scene::GetComponent(EntityID)' 的引用
此错误出现 3 次,每次 T 等于 Name、Transform 和 Sprite(到目前为止我的游戏引擎中的每个组件)
MSDN 上的编译器错误 C2440:https ://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2440?view = vs-2019
我的实体组件系统通过两个数组数组存储实体和组件。
第一个是componentIndexes,用于按实体索引到第二个数组中,第二个数组components将根据索引返回一个组件。从本质上讲,如果您听说过使用“稀疏”数组和“密集”数组来压缩数组,那么这 …
c++ components compiler-errors game-engine entity-component-system