ECS
Entity、Component、System
- Entity
- Component
- System
模块解耦
从描述的状态上,不同的观察者会看见不同的行为,拆分不同System出来分别实现。
内存连续
通过合理组织数据利用 CPU 的缓存机制来加快内存访问数据。
举个例子,当在 Unity 中实时更新若干个 Monster 和 Player 的坐标时,往往是在 Update 中实现 transform.position = new Vector(1,2,3),即需要的数据是 Position,但是却把整个 Transform 数据加载进内存,更高几率造成 Cache Miss;此外,对应 Monster 和 Player Update 生命周期更是不可控,导致加载的数据不久后就失效,一定概率造成 Cache Miss。
上面例子中对数据的处理方式对 Cache 不友好,也对 CPU 访问内存的时间局部性不友好。
Entitas
Entitas 组成
(1)Component
Unity Component 包括数据和行为。
ECS Component 单纯包括数据。
(2) System
Entitas 中的 System 等价于方法。
System 存在以下四种类型:
System 类型 | 实现功能 | Unity 相似函数 |
---|---|---|
Initialize System | 初始化功能 | Unity Awake |
IExecute System | 每帧更新功能 | Unity Update |
Reactive System | 触发式更新功能 | Unity OnCollider 等 |
TearDown System | 析构功能 | Unity OnDestory |
其中 Reactive System 比较特别,主要实现是通过 Group + Collector 组成,通过监听特定 Component 来处理对应的 Entity,避免需要每帧处理大量数据。
Entitas 跟 Unity 交互
Unity 通过脚本构建对应的 Entity,并添加对应的 Entity Component来传递消息,经过 Entitas 处理数据后通过监听函数返回 Unity。
录像回放机制
由于 Entitas 本身是严格时序控制,调用方在保证浮点数误差,随机数等一致的情况下,对于同一个输入源,其输出结果也是一致的。
性能
通过图片可以看出,Entitas 在性能上要优于 Unity。
- reactive 标签表示通过开启 Reactive System 处理。
- non reactive 标签表示不通过 Reactive System 处理。
- Job System 标签表示通过开始线程处理。
关闭Reactive System
Entitas 中如果某个 Component 通过 ReplaceXXX 函数赋值,则 Reactive System 会收集到对应的 Entity,如果通过 XXX 直接赋值,则 Reactive System 不会收到对应的 Entity,从而提高性能,但是在实际应用中,不会专门绕开 Reactive System。
对象池
Entitas 通过尽可能复用所有对象来提高性能,减少内存分配时间和碎片。
- 复用 Entity
- 复用 Component
- 索引 Components
- 索引 ComponentValue( EntityIndex )
- 复用 Groups
ECS 编写模式为 System 中处理一批相同 Component 类型的数据,导致每次处理某个特定值时,需要检查全部相同类型 Component 的 Entity,因此引入 EntityIndex 以高效解决定位拥有 Component 某个具体值的 Entity。
Job System
Entitas Job System 跟 Unity Job System 实现方式不一致,其中Entitas Job System 是通过手动开辟新的 Thread。