在 Richard Fabian 的Data Oriented Design中,他给出了一个基于组件的游戏对象示例,其中对象可以具有的不同属性被分成不同的数组,并通过处理这些数组中的数据来创建功能。他举了一个渲染的例子:
struct Orientation { vec pos, up, forward, right; };
SparseArray<Orientation> orientationArray;
SparseArray<Vec> velocityArray;
SparseArray<bool> isVisible;
SparseArray<AssetID> modelArray;
void RenderUpdate() {
foreach( {index, assetID} in modelArray) {
if( index in isVisible ) {
gRenderer.AddModel( assetID, orientationArray[ index ] );
}
}
}
我感到困惑的是这段代码是如何对缓存友好的。据我了解,面向数据的设计通过将特定操作所需的相关数据保存在连续内存中来实现更好的性能,因此当您迭代数据进行更新时,您将减少缓存未命中的数量。然而,在这个例子中,渲染需要来自 3 个不同“组件”的信息,因此在每次迭代中,您将访问不同的数据数组,更不用说如果任何组件需要与每个组件交互,则必须查找 ID 可能会影响性能别人的数据。
我正在开发一个非常简单的 2D 游戏来练习面向数据的设计。如果我要在我的游戏中模拟一个基本行为,比如让对象按照上面给出的模式移动,它会是这样的:
struct Position { int x, y; };
struct Velocity { int x, y; };
SparseArray<Position> positionsArray;
SparseArray<Velocity> velocitiesArray;
然后在物理更新中,我会用相应的 in更新每个Position
in 。positionsArray
Velocity
velocitiesArray
缓存将这个操作所需的数据组合到一个结构中不是更好吗:
struct MoveComponent { Position pos; Velocity vel };
SparseArray<MoveComponent> moveComponents;
所以您拥有更新所有连续存储的位置所需的数据?