层次组件的问题
最近思考了 ECS 框架中的一些问题。
具体业务中,有许多组件的构成非常复杂,本质上数据是有层次结构的。用一个二维表的结构很难清晰表述。它还牵扯到另一个问题:是否需要支持一个实体有多个统一类型的组件。例如,玩家实体身上多个装备栏、多个技能 Buf 该如何表达?
我有一些不成熟的想法,还没有具体落地,先记录一下:
在目前的 ECS 框架中,我以内存数据库的形式管理所有的数据。把 Entity/Component (实体、组件)看成是一张二维稀疏表。每个 Entity 占一行,每类 Component 占一列。整张表为一个 world 。
而我最近的反思是,一个世界不应该只有一张表,没有理由把所有数据都塞入同一张二维表。
以物品栏 Inventory 为例,每个角色都会有一个 Inventory ,Inventory 是由诸多物品格 slot 构成的。这就是一个有层级关系的数据结构:角色 -> 物品栏 -> 物品格。
如果我们去掉物品栏这一层抽象,那么,角色这个 Entity 上就需要有复数个物品格 Component 。
通常,如果我们有一个系统 system 是用来处理身上携带物品对角色起的效应,那么最好在 select 物品时,可以把同一个角色身上的所有物品聚合在一起处理。它们之间是有关联的。
相比把这些数据塞入一张大表,一个可能更好的模式或许是为所有的物品格单独建一张表。在这张表中,Entity 指的是单个物品格,同一物品格可以有多个 Component 和 Tag 。有一个 Component 保存的是它的 id ,用来和存放角色 Entity 的那张表发生关联。
在角色 Entity 的表中,我们只维护它身上有几个有效的物品格,真正的数据都放在物品栏的专用表中。
在物品栏的专用表中,为每个物品格生成一个唯一 id ,这个 id 是由角色 id 以及物品格在该角色的 Inventory 中的编号构成的。
使用 ECS 的 select 已经实现的 order 模式(一种特殊的 select 方式,可以根据特定组件 id 排序,以特定的次序而不是构建持续遍历),我们可以做到把同一个角色身上的所有物品格聚在一起遍历。
同时,还可以给物品格打上 tag ,select 时可以挑选出关注的那一部分:例如可以按物品用途分类等。
支持多张表并不需要修改已经写好的 luaecs 的实现,只是原有的名字起的不恰当。单张表不应称为 world ,而需要再加一层 world 来管理多张表。
把数据拆分成多张表后,底层也可以对 order tag 做进一步的优化。因为原有的 order 模式虽然可以让 select 以特定次序遍历 entity ,但相比普通的按构造次序顺序遍历 entity ,查询 entity 的 component 的时间复杂度有可能从 O(1) 下降到 O(log N) 。
我们可以增加一个方法让使用者主动定期按 order tag 整理一张表的内部内存实际布局,提高顺序访问的效率。整个整理工作代价比较大,但分拆数据到多张表可以减少很多整理成本。
Comments
Posted by: a | (5) May 17, 2022 05:09 PM
Posted by: rawa459 | (4) May 2, 2022 02:21 PM
Posted by: 403city | (3) April 2, 2022 12:32 PM
Posted by: EOS. | (2) March 25, 2022 02:53 PM
Posted by: Xuan。 | (1) March 24, 2022 06:01 PM