1

我的基于组件的游戏引擎的抽象实现GameObject如下:

游戏对象

  • 唯一身份
  • isActive 标志
  • 组件数组

我一直在阅读有关基于组件的设计,引起我注意的一件事是由于缓存,数组的线性遍历很快,所以这让我决定使用基本数组来保存实体/游戏对象。

现在引起我注意的另一件事是一些资源将组件与游戏对象分开。相反,他们所做的是在实体管理器中放置一个地图。该地图将游戏对象作为键,将一组组件作为值。在这种情况下,AGameObject只有一个 ID。

将游戏对象附加到组件而不是在类中包含组件是否有优势(性能和/或设计方面)GameObject

另外,GameObjects 会从一个Pool对象中池化(回收),以避免频繁的内存分配。

4

2 回答 2

3

在大多数游戏组件系统中,您将需要数组和字典(地图)。

对于线性遍历(即在更新期间),该数组用于枚举对象和组件。

对于按 ID、名称或其他属性查找,可以使用一个或多个字典按相应类型查找组件。这在对数组进行线性搜索会导致明显的性能损失时最需要这样做。

此外,可能还有其他数组存储按组件排序的游戏对象,例如具有特定组件(如 CombatComponent)的游戏对象数组。

通常这将被包装在一个类中,允许您通过索引访问组件(或游戏对象)、枚举它们或通过某些条件(例如 ID)获取它们。然后可以随着时间的推移更新类的实现,以通过以更高的内存使用率换取更好的性能来加速对特定组件的访问(即引入额外的字典来按启用状态索引组件,或按空间位置索引游戏对象)。

简而言之:总会有取舍。开始使用额外的字典和数组主要是为了方便和性能,但这绝不是一个非此即彼的决定。

如果需要按顺序枚举对象,则默认存储实现应始终以数组开头。字典的主要问题是枚举的顺序可能会逐帧改变,这在游戏中必须避免。

于 2014-07-22T17:33:05.083 回答
1

这实际上还取决于您制作的游戏类型。

根据我的经验,松散耦合的对象几乎总是表现得更好,因为内存分配很少发生,而引用对象通常分批发生,并且用于特定任务。我喜欢这种方法,因为它可以防止你弄乱一大堆虚拟表和类层次结构。虚拟指针会破坏代码缓存。因此,在这种情况下,我将对象放在不同的管理器中,但具有相同的句柄/ID,并且只在相关集合中运行。

如果涉及昂贵的可见性查询,保留多个列表并仅在必要时更新它们通常更便宜(例如时间片)

另一种方法是使用仅与相关对象具有父/子关系的链表来构建依赖遍历树(一种基于树的跳过列表)。这很有用,因为有时您想预先构建变换,或循环遍历所有发光体,或首先通过碰撞检测和响应等运行所有对象。从逻辑上讲,任何类型的“事件”都必须发生在您的然后可以在链表遍历中翻译游戏世界。它为您节省了管理人员,但它创建了大量的内存跳转和管理,以使父/子保持最新。

如果您可以矢量化您的计算并获得 100 倍的加速,那么这就是查看对齐和打包的好理由。有一次,我们使用了一个小型对象缓存,只在内存中保持那些经常需要的位对齐,例如转换等。但实际上,在你到达之前,你最终会调用这么多 isXYZ()转变,这并没有真正得到回报。这个想法很好,当时有一些加速,但逻辑复杂到它变得毫无意义的地步。

正确的说法当然是它取决于任务以及您必须处理的对象或其子集的数量。如果您一开始不知道这一点,但您怀疑它可能会失控,这取决于您拥有的时间:如果很多,松散耦合可能就是问题所在。额外的好处是重构类型并不会真正对您产生太大影响。否则,您最好将所有内容存储在一起并希望获得最好的结果。

于 2014-08-27T12:25:13.450 回答