c++ - 关于纯 ECS(实体组件系统)和更新系统的问题
问题描述
我已经写了一个 ECS,但我对更新阶段有一些疑问。(在系统中)我读过很多文章,但没有找到对这类问题的引用。
为了从 ECS 中受益(例如缓存友好),它们是以下要求:
- 实体必须只是一个 ID。
- 组件必须是纯数据(没有逻辑的结构)。
- 系统包含逻辑并更新组件。
- 系统之间没有交互(而是系统通过向实体添加“标签”组件进行通信)。
因此,每个系统中应用的逻辑都很好,并且在它们不是“用户代码”时都可以正常工作。但是,当我们处理用户代码时(例如,用户可以将 C++ 代码附加到对象(如 Unity、Unreal)),问题就来了:
- 由于组件只包含数据,当用户修改本地位置时,世界位置不会更新(世界位置将在Transform System处理每个Transform Component时计算。所以如果用户在修改后询问世界位置它的本地位置,它将获得以前的世界位置,而不是实际位置。
- 当一个实体被移除时,它的孩子必须被移除。由于组件仅包含数据而不包含逻辑,因此不会删除子组件(它将在下一次父系统更新中)。所以我们有一些“延迟”(孩子仍然可以访问,但将在下一次父系统更新时被删除)。
- 假设我们有实体 A、B、C。B 是 A 的子代。在用户代码(实体附加的 c++ 代码)中,用户设置 B 的父代有 C,然后删除实体 A。当父系统将更新,它会检测到 A 已被移除,(它也可以检测到实体 A 的父级已更改)但是系统如何知道实体 A 是否在实体 B 的父级更改之后或之前已被删除?
在组件中添加逻辑将破坏纯 ECS 的优势(以缓存友好的方式对所有相同的组件执行相同的操作),所以恕我直言,这不是一个解决方案。
有人有解决方案吗?我想知道您如何处理 ECS 实施中的此类问题。
谢谢!
解决方案
我和你有同样的问题。
阅读后我模拟了我的解决方案(这是必须诚实阅读的):
Gamasutra:将面向数据的 ECS 与有状态的外部系统同步
一个可能的解决方案是设置一些关于读写组件的规则。
我遵循从组件数据中读取总是可以的规则,但是如果您必须将数据写入外部系统内的组件(它不是组件接口系统的一部分),您必须始终使用转换系统功能。
例如:
有一个像这样的转换组件:
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 编程。你拥有可以更改这些数据的数据和函数。当然,您可以直接更改组件数据,但我意识到花时间试图避免这种情况是不值得的,如果您在更新数据后需要更多东西的组件中进行操作,这只是一个错误或糟糕的编程。
推荐阅读
- docker - 如何从其他网络访问 docker 容器(apache 服务器)上的网站?
- c++ - 是否有格式处理器可以编写我自己的类似 printf 的函数并保留 %d 样式参数,而不使用 sprintf?
- android - 如何获取使用 Firebase 云消息传递向其发送通知的设备或用户的列表?
- android - 如何制作仅适用于电视和盒子 android 的 apk
- reactjs - 根据给定的道具渲染一个元素,采用适当的元素属性
- javascript - 生成具有随机值 TypeScript 的二维数组
- python - 在 Django 中创建超级用户时出错
- javascript - 在私有路由 React 中传递道具
- swift - 图像上的 SwiftUI .cornerRadius 未显示
- python-3.x - 如何从 python 'webbrowser' 获取 URL?