自从去年实现了ECS之后,发现有几个比较大的问题一直没有解决,一个就是TEntity<...>,这样不同的entity有
不同的类型,和主流不太一样,同时导致修改entity的类型比较麻烦,必须重新写一个对象来赋值,不符合常识。
我多次试图删除类型却失败了。
同时因为当时的模板只用到c++17,c++20的一些特性没有用进去,导致实现 模板 lambda 较复杂。
后面因为其他工作导致这项工作中止。
一年之后,机缘巧合解决了以上几个问题,我的ecs架构做了几个更新。
读者可以参考我之前写的一篇ECS文章,这里贴上链接
主要更新如下:
1,将TEntity<...> 简化为了 Entity, 这样可以直接修改Entity的原型了,同时和主流保持一致。新对象和老对象复用同一个entity。
2, 将EntityTable从Archetype中移动到了EntityMgr中。先前实现有个问题,就是在修改entity的类型时,entity的索引会改变,导致外部引用可能全部失效。
3,代码做了大幅度优化,引用了c++20模板技巧简化了实现,同时去掉了冗余代码,将所有代码合并为单一头文件实现,整个代码量从原来的几千行,压缩到了不到700行,据我观察应该是史上最简洁的ECS实现。但是麻雀虽小,五脏俱全。
5,参考了flecs中的 原型转换图 的思路,它的实现方法是 在可以通过 添加或删除组件的原型之间增加一条有向边来快速索引。而我的实现直接建立原型静态二维表格,速度更快。同时,这种实现的另一个好处是,可以有效避免中间态原型的出现,比如[A,B],[A,B,C,D,E],如果从第一种转到第二种,按照主流实现方法只能依次添加三个组件,这样会产生不必要的中间态原型[A,B,C],[A,B,C,D],而且这些转换成本没有意义。本实现直接在原型之间跳转,解决了这个问题。
ECS的主要实现方法大致有两种:1,以Unity为代表的原型实现方法,这也是许多ecs采用的方法 2,以entt为代表的稀疏集实现方法,
1,第一种实现遍历很快,修改entity类型会有损失 2,第二种实现正好相反。
本实现采用1中的 原型方案实现。
不过值得一提的是,即使是原型实现,也会用到稀疏集。可以说稀疏集是ECS实现中几乎不可避免的一个环节。
目前世界上所有的ECS实现,无一不涉及到大量 的运行期hash,这些主要是把类型转为id,然后id索引之类的操作。由于本实现采用静态注册的方法,没有运行期hash成本。另外,它很可能是世界上最简洁的ECS实现,总共代码只有单头文件不到700行,(不含三方库,但是三方库只有两个依赖,一个是原本的taskflow用来并行化,第二个是单头文件的TTuple)并且它完全符合 unity的原型 的架构,在功能上没有任何损失。
这里贴上链接,欢迎加星或者提出问题: