我看到了 Mike Acton 的“面向数据的设计和 C++”,我觉得它很有趣。我不明白如何解决数据依赖关系。
想象一下,我有一个简单的 2d 引擎: * 物理数据 - 处理物理 * 图形数据 - 渲染精灵 * 声音数据 - 播放声音
图形数据和声音数据取决于存储在物理数据中的位置。可以从物理数据中引用位置,但在我看来,这会扼杀 DOD 的全部意义——在同一内存位置拥有所需的数据。
在面向数据的设计中如何处理这种情况?
我看到了 Mike Acton 的“面向数据的设计和 C++”,我觉得它很有趣。我不明白如何解决数据依赖关系。
想象一下,我有一个简单的 2d 引擎: * 物理数据 - 处理物理 * 图形数据 - 渲染精灵 * 声音数据 - 播放声音
图形数据和声音数据取决于存储在物理数据中的位置。可以从物理数据中引用位置,但在我看来,这会扼杀 DOD 的全部意义——在同一内存位置拥有所需的数据。
在面向数据的设计中如何处理这种情况?
DOD 更像是一种设计架构的通用方式,它首先关注如何有效地表示数据。没有单一的方法可以做到这一点。Linus Torvalds 对 Linux 内核和 Git 等表现出了这种心态,但它与游戏是一个非常不同的领域。最主要的是,他首先专注于如何有效地表示数据。
作为一个基本示例,如果您正在设计一个图像处理应用程序,那么如果您不是以面向数据的方式思考,而是专注于如何最轻松地支持最广泛的像素格式并提供最简单的接口使用,您可能会想出一个抽象的Pixel
,甚至可能是每个像素的堆分配。此时,您将支付虚拟指针(通常大于像素本身)、每像素动态调度、可能是另一层间接性以及可能完全丧失空间局部性的成本。相反,如果您首先考虑如何有效地表示数据,您可能会更粗略地抽象Image
级别(像素的抽象集合,可能是给定图像的数百万像素),如果您在不为每个像素级别的此类开销支付费用的情况下进行抽象。
也就是说,对于游戏而言,处理您所谈论的内容的常用方法通常是使数据可集中访问。这似乎违反了 SE 原则,但通常如果您使用实体组件系统之类的东西,任何给定类型的组件通常只能由少数系统访问。结果,该数据的范围往往足够小以有效地维护不变量。
至于游戏中可能发生的事件,比如两个实体在物理系统中相互碰撞,声音系统可能想要播放声音,有很多方法可以解决这个问题,以使物理系统和声音系统彼此分离其他。一种是使用事件队列。
至于一个系统所需的数据也被另一个系统共享,这通常是相当实用的。如果你想并行运行这些系统,它们仍然必须复制共享数据,可能会更新它,并以某种方式协调它们的结果。也就是说,在我看来,避免摆弄它并并行化系统正在做的事情(例如:使用并行 for 循环)会更有效率,因为通常 ECS 中只有少数几个系统是热点并且做真正的繁重工作,您可以轻松地将这些特定系统的工作分布在线程之间,而无需尝试同时运行许多系统并打开蠕虫罐。
不确定是否完美 DOD,但如果您的 ID 或句柄在整个物理和图形子系统中共享,您可以让物理子系统生成所有更新对象的位置和 ID/句柄数组,并将其用作图形子系统的输入.