我想知道如何用 C++ 实现最快版本的实体组件系统(ECS 从现在开始)。
首先,关于术语:
- 场景是实体(以及某些实现中的系统)的容器
- 组件是一个简单的数据存储(如位置、碰撞框、要渲染的图像等)
- 系统对符合系统要求的组件执行逻辑(这可能是物理、玩家输入、简单渲染等)
- 一个实体包含几个组成最终行为的组件
我在下面列出了我们提出的所有设计。
1.“幼稚”的方式
场景包含所有无序的实体。
随着系统的更新,每个系统都必须遍历所有实体并检查每个实体是否包含所有必需的组件,然后对这些实体执行更新。
显然,当有很多系统和/或很多实体时,这种方式的性能不太好。
2.使用“位掩码枚举”和映射
每个组件都包含一个位掩码形式的类型标识符(例如1u << 5
/ binary [0...]100000
)。然后每个实体可以组成所有组件的类型标识符(假设所有类型 ID 在实体内部都是唯一的),所以它看起来像
1u << 5 | 1u << 3 | 1u << 1
binary [0...]101010
场景包含某种地图,系统可以在其中轻松查找合适的实体:
MovementSystem::update() {
for (auto& kv : parent_scene_) { // kv is pair<TypeID_t, vector<Entity *>>
if (kv.first & (POSITION | VELOCITY))
update_entities(kv.second); // update the whole set of fitting entities
}
}
优点:
- 比天真的方式更快
缺点:
- 系统每次更新时都必须查找适当的实体。
- 位掩码(枚举)被限制为位数(32 位
uint32_t
,至少 64 位unsigned long long
),在某些情况下,您可能需要比位掩码允许的更多组件。
3. 不使用系统
优点:
- 完全摆脱位掩码。
- 可能比设计#2 更快。
缺点:
- 依赖
dynamic_cast
于查找组件,而设计#2 可以直接查找组件,然后安全地查找static_cast
它。