我已经写了一个 ECS,但我对更新阶段有一些疑问。 (在系统中)
我已经阅读了很多文章,但没有找到对此类问题的引用。
为了从 ECS 中受益(例如缓存友好),它们有以下要求:
向实体添加“标签”组件)。
因此,每个系统中应用的逻辑都很好,并且在它们不是“用户代码”时都可以正常工作。
但是,当我们处理用户代码时(例如,用户可以将 C++ 代码附加到对象(如 Unity、Unreal)),问题就来了:
本地位置,世界位置不更新(世界位置
将在转换系统处理每个
变换组件。所以如果用户在之后询问世界位置
修改它的本地位置,它将获得以前的世界位置
而不是实际的。
组件只包含数据而不包含逻辑,子组件将
不会被删除(它将在下一次父系统更新中)。所以我们
有一些“延迟”( children 仍然可以访问,但将
在下一次父系统更新中删除)。
用户代码(实体附加的c++代码),用户设置父级
B 有 C,然后删除实体 A。当父系统将
更新,它会检测到A已被删除,(它也可以检测到
实体 A 的父级已更改)但系统如何知道
如果在实体 B 的父级更改后实体 A 已被移除
还是以前?
在组件中添加逻辑将破坏纯 ECS 的优势(以缓存友好的方式对所有相同的组件执行相同的操作),因此恕我直言,这不是一个解决方案。
有人有解决方案吗?我想知道您如何处理 ECS 实现中的此类问题。
谢谢!
最佳答案
我和你有同样的问题。
阅读后我模拟了我的解决方案(这是必须诚实阅读的):
Gamasutra: Syncing a data-oriented ECS with a stateful external system
一个可能的解决方案是设置一些关于读写组件的规则。
我遵循从组件数据中读取总是可以的规则,但是如果您必须将数据写入外部系统内的组件(它不是组件接口(interface)系统的一部分),您必须始终使用转换系统功能。
例如:
有一个像这样的转换组件:
struct transform {
glm::vec2 position = glm::vec2(0);
glm::vec2 scale = glm::vec2(1);
float rot_radians = 0.0f;
glm::mat3 ltp = glm::mat3(1);
glm::mat3 ltw = glm::mat3(1);
entt::entity parent = entt::null;
std::vector<entt::entity> children;
};
我将定义一些系统来对其进行更改,如下所示:void set_position(entt::registry& r, entt::entity e, glm::vec2 position);
void set_rotation(entt::registry& r, entt::entity e, float rot_radians);
void set_scale(entt::registry& r, entt::entity e, glm::vec2 scale);
void set_parent(entt::registry& r, entt::entity to, entt::entity parent = entt::null);
在这些函数中,您可以自由地读取/写入转换组件数据。我使用 ECS 的次数越多,我就越倾向于认为自己是在用 C 编程。你拥有可以更改这些数据的数据和函数。当然,您可以直接更改组件数据,但我意识到花时间试图避免这种情况是不值得的,如果您在更新数据后需要更多东西的组件中进行操作,这只是一个错误或糟糕的编程。
关于c++ - 关于纯 ECS(实体组件系统)和更新系统的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59787456/