Unity Ecs原理分析
- 前言
- 一、ECS是什么?
- Entity是什么?
- Component是什么?
- System是什么?
- 不得不提的Archetype
- 为什么时16kb?
- 什么是Structural Change?
- ASpect
- 有关ECS使用时的安全性
- Conversion World & Shadow world
- 总结
前言
Unity 的ECS 是Unity DOTS的组成部分,是编码的重要部分,DOTs的其他方面还包含Brust Compiler等等。
一、ECS是什么?
ECS是Entity,Component,System 的简写,这三个便是ECS最核心的组成部分,常规的,开发者在使用Unity进行游戏开发时采取面向对象的编程思想,使用非常直观的GameObject,GameObject上面添加的组件以及脚本控制,在运行时在场景中初始化,改变物体的行为等等。ECS架构采用的是面向数据的编程思想,通过对数据的合理化排列以及组合,充分利用Cpu缓存命中,充分利用内存,将组件分类并在使用时直接通过改变组件的数据使得场景中的游戏物体Transform。
Entity是什么?
Entity 是一个将独立组件关联起来的ID,而不是将其包含在一起的容器。也就是说Entity并不负责存储数据,仅是作为唯一ID将相互独立的组件关联起来,Entity默认情况下被存储在一个World中(默认情况下一个Scene也仅包含一个可操作的World(后续会提到开发者不能直接操作的World)),EntityManager可以创建,销毁和更新Entity。
Component是什么?
Component就是组件,负责在ECS架构下存储数据,ECS架构下,有默认的组件如Transform等,开发者也可以根据需要通过组合和设计来创建新的组件,并在Baker中进行Baking。
System是什么?
System 用于将Component上的数据从一种状态转换到另一种状态,System运行在主线程的每一帧上。
不得不提的Archetype
在Entity介绍中提到,Entity将独立的组件关联起来,关联起来的Component便可以组合成一个ArcheType,而相同的ArcheType在ECS架构中会以16kb为单位存储成Chunk。
为什么时16kb?
16kb无论是分页内存还是Cpu寻址最大范围抑或是Cpu缓存L1时16k的整数倍,16kb的选择既是出于对数据存储大小考虑,更是对Cpu缓存命中以及数据对Cpu 的易操作性的考量,使得Cpu极大程度的发挥它的能力。
提到内存利用就要展开聊一下Chunk,如图:
如上图所示,组件和Entity在chunk中以16kb为一个chunk紧密的排列在一起,所以在查询和遍历时开发者才可以如此迅速的找到对应的Entity以及所关联的Component,在对游戏场景内Entity关联的Component进行数据更改时效率会非常高。
什么是Structural Change?
结构化更改既在对Entity与其组件的更新过程中,对Chunk中的存储数据进行了删除,插入,以及对ShareComponent 修改了值。从而破坏了原本的chunk结构,经过更改后,原本的存储结构更新,而这样的更新是非常消耗的, ECS架构依赖于CPU充分利用缓存机制的同时,也充分利用多线程,显然的,chunk是在线程中共享的,Structural change发生,如果不加以处理,很容易造成空悬引用和位置错误,所以Unity引入了Sync Point。既然有同步点,就有同步机制,有同步机制就有线程等待等等的操作,从而大幅降低了当前帧的运行速率。
而在ECS架构的使用过程中,不可避免的需要有结构化更新来使得内容更加丰富,这就引入了EntityCommandBuffer,使用EntityCommandBuffer可以将耗时操作分帧进行,从而降低帧间隔。
ASpect
Aspect在C#代码中可以将component 结构化的编辑到一起,在查询遍历时会非常的方便,在使用过程中必须添加readonly,readonly并不代表Aspect是只读的,真正起作用的是RefRO & RefRW,提供了只读和读写访问,特别需要注意的是,当引用一个Aspect时请使用in 对应ReadOnly数据,使用ref对应ReadWrite数据。
有关ECS使用时的安全性
Entities包的许多内部API使用unsafe code block和原始指针来获得最佳性能。一些API返回对数据的引用,这些引用可能比引用的数据更持久。Job System 也并不保证数据的正确性和安全性,所以在使用时开发者在使用JobSystem时需要自行注意数据的安全性。
Conversion World & Shadow world
还记得最开始提到的world么,ECS架构中,将World按照主要用途分为两大类,一类是 Conversion World,用于对常规Gameobject以及在Baker中编辑的Authoring Data转换为Entity以及与之相关联的Component。而转换分类为full baking & incremental baking,living baking通常在开发者更新subcene内容时因改变而全量baking --> 从无到最新的过程,而incremental baking,使用Shadow World记录最新一次的更新,并在下一次更新时只baking增量改变而不是全量baking,也正因如此,原始baking的数据不变,可能会使得full baking 和 incremental baking之间存在差异。
总结
以上便是在开发过程中非常重要且常用得几个点,当然因为ECS与原始得Unity处理方式并不兼容,Unity也为physics,render都分别提供了不同的包用于针对于ECS架构的开发,ECS + Brust Complier + JobSystem 在复杂游戏效果中更加出众,诸如弹壳特工队便是使用DOTs + GPU Instancing 来实现的。
后续文章会将Brust Compiler + Job System 补全