DOTS
Unity DOTS是Unity官方基于ECS架构开发的一套包含Burst编辑器和JobSystem的技术栈,它旨在充分利用多核处理器的特点,充分发挥ECS的优势。
安装
Entities、Burst、Jobs、Hybrid Renderer(必选,用于DOTS的渲染相关)、Unity Physics(非必选,用于DOTS的高性能物理组件)
com.unity.Entities
com.unity.rendering.hybrid
ECS框架
实体(Entity):ID和组件列表
组件(Component):存储Data
系统(System):对象关联的Component进行操作
适用情况:需要处理超多对象的同屏渲染问题,如戴森球计划;需要处理超多对象的管理和操作,如FPS游戏。
传统Unity开发,通常是把逻辑脚本挂载到GameObject上,很多时候还可能挂载多个不同功能的逻辑脚本。而ECS采用面向数据的方法,Entity代替了传统的GameObject的概念,Component则保留了传统组件的数据部分,所有逻辑交由System处理。写ECS的基本思路就是创建Component、创建Entity、编写System处理逻辑。
System可以重写OnUpdate函数,大家可以把它看成是MonoBehaviour的Update函数。这就是之前提到的,ECS把实体的逻辑专门放到System里处理了。OnUpdate函数也是会每帧调用一次的。
Component继承IComponentData
Entity
创建方式
- [GenerateAuthoringComponent] 自动将组件转换成实体
- [RequiresEntityConversion] 通过EntityManager的AddComponentData将组件附加到实体上
- 通过Prefab创建实体 *方便
- EntityCommandBufferSystem *灵活
Component
Component需要继承IComponentData
其他组件类型
ISharedComponentData(共享组件) // 生成大量相同怪物
ChunkComponent(块组件)
ISystemStateComponent(状态组件) // 处理回调事件
ISystemStateSharedComponentData(状态共享组件)
IBufferElementData(动态队列/缓冲区/数组)
System
System需要继承ComponentSystem
或JobComponentSystem
ComponentSystem
更适合旧项目迁移到ECS框架,类似于Monobehaviour,但ECS的性能在多线程下才能发挥到最大JobComponentSystem
配合各种Job(IJobForEach、IJobChunk等),可以方便地实现并行(多线程、多核)执行逻辑
System中可以重写了OnUpdate(),该函数每帧调用一次,类似mono的Update()。System相当于将一定的逻辑封装起来,例如RunSystem、JumpSystem等,那对应的问题就是System如何知道哪些Entity需要执行逻辑,答案就是筛选,而筛选条件就是Component。
筛选方式
Entities.ForEach
IJobChunk
IJobForEachWithEntity
EntityQuery
*灵活 All Any None
Entity Debuger
不同于传统Debug,ECS框架下需要用Entity Debuger进行调试,Unity菜单的Window->Analysis->EntityDebugger,可以打开ECS的调试窗口
Chunk和Archetype
对于拥有相同Component的Entity,ECS采用Chunk结构统一存储,例如下图中的EntityA和EntityB
Chunk不能无限存储Entity,当Entity数量过多时,就需要用新的概念Archetype来统一保存Chunk。
World
ECS会自动创建一个默认世界,包含了实体管理器(EntityManager)以及项目中所有可用的系统(System)。
系统分组(ComponentSystem Group)
System用来处理游戏逻辑,游戏逻辑肯定就会涉及到先后顺序,为了解决这类问题,System的OnUpdate函数的执行是有先后顺序的。
// ECS默认分组
InitializationSystemGroup(负责初始化工作的系统分组)
SimulationSystemGroup(负责逻辑运算的系统分组)
PresentationSystemGroup(负责图形与渲染工作的系统分组)
使用特性(Attribute)调整System执行顺序
UpdateInGroup:指定当前System在哪个分组下
UpdateBefore:指定当前System在哪个System之前执行
UpdateAfter:指定当前System在哪个System之后执行
EntityCommandBuffer
JobComponentSystem配合各种Job(IJobChunk等),可以方便地实现并行(多线程、多核)执行逻辑。多线程逻辑中会出现数据一致性的问题,ECS规定,以下行为都不能在Job中处理:创建实体(Create Entities)、销毁实体(Destroy Entities)、给实体添加组件(Add Components)、删除实体的组件(Remove Components),需要采用EntityCommandBuffer提前或延迟执行。
每一个系统分组下都有两个EntityCommandBufferSystem,并且分别都是Begin和End对应的,通过World获取System。
笨木头的博客里讲到“在JobComponentSystem中,如果Job没有筛选出实体数据,那么,OnUpdate是不会被调用的”,这也就解释了为什么有些system最后会执行DestoryEntity操作。这就是面向数据编程的关键,程序根据数据筛选实体,执行逻辑,删除数据保证后面筛选不到。(我需要执行逻辑就添加,执行完了删掉,完全是数据有无在控制逻辑的有无)。
EntityCommandBufferSystem每次执行队列的任务后,都会清空,所以不存在重复执行的问题。
参考链接
官方Demo ECS Samples
笨木头与游戏开发 博客