编写本文时,Entities 包更新到了 preview 1 - 0.11.2 版本
主要参考官方文档,同时结合我自己的一些开cai发keng经历,建议英语好的同学直接去啃English — ECS Docs
Demo List
目录
一、什么是 ECS
ECS,全称 Entity Component System(实体、组件、系统)。区别于传统的通过继承 MonoBehavior 的类并重写 Start() Update() 等来控制 GameObject,ECS 使用 System 来修改 Entity 的 Component 数据来实现对象控制,为开发者提供了一套能够使程序高效运行的开发模式。
用官方的话说,The Entity Component System (ECS) is the core of the Unity Data-Oriented Tech Stack. 翻译过来,实体组件系统(ECS)是Unity面向数据的技术堆栈的核心。
World, Entity, Component, System
-
World(世界): ECS中有一个特殊的存在–World,当程序启动时,默认会生成一个World 。当然如果你有一些特殊的需求(比如在Online Game 中重放死亡、击杀镜头),可以用到多个World 对象。在一个World 中创建的Entity 仅在该World中有意义,但是可以通过EntityManager.MoveEntitiesFrom 迁移到其他世界。
每个World 都有一个EntityManager,负责管理该World 中所有的实体,同时还包含许多System 用来执行程序的逻辑。
-
Entity(实体): 实体表示程序中的单个物体,这里的实体并不是指具体的游戏对象,而是指代一个概念,实体并不包含具体的数据、行为,它主要用来关联一些列的Component 数据,可以理解为一个索引值。
ECS系统维护这些索引值,并且可以通过索引值来获取到Entity 对应的Component 的数据,并通过System 进行高效的读取、修改等操作。
-
Component(组件): 是一个struct,并且一般需要继承IComponentData(通用组件,当然还有类型的组件)才能够被ECS所识别。这里的组件与Unity Editor 中的并不一样。Unity Editor 可以在GameObject 上添加各种组件(比如TransForm, Animator, Script…),但是ECS中的组件只是一系列的结构体,并且只用来存放数据。
Component 可以在Unity Editor 作为Script 挂载到游戏对象上,也可以在代码中使用EntityManager 进行组件的添加、修改。
-
System(系统): System是ECS中实现几乎所有代码逻辑的部分。在System 中,我们可以对Entity 进行查找,并对Entity 上的Component 数据进行处理。通常来说,我们的System 类需要继承自SystemBase 并且重载OnUpdate()方法。
System 通常被用来更新Entity 的状态。比如,Entity 挂载了一个名为MoveSpeed 的组件,其中保存了Enttity 的移动速度,System通过读取该组件的值,然后修改Translation 的值来实现Entity 的移动。
当我们在项目中编写System 代码时,无需将它挂载到某个GameObject身上,因为ECS在运行时,会找到所有可用的System 并且实例化它们,将其添加到默认World 中的系统组,关于System Group 的概念我们会在后面说道。
以该图为例,System 读取Entity A 和 B 的(Translation–位置, Rotation–角度)组件数据,并且进行计算,最后对两个实体的LocalToWorld 进行数据的更新。
这里就体现了ECS 的与众不同,具有相同的组件的实体会被放置在相同或相邻的内存块,使得System 能够对同一种类型的实体进行快速的查找,并且能够实现并行的数据处理,从而提高了程序的执行速度。
Chunk Archetype
-
Archetype(原型): 一系列组件的唯一组合称为原型Archetype,换言之拥有完全相同的组件类(数据可以不相同)型的两个实体属于同一种原型。
Archetype 所对应的Entity Component数据都存储在同一片区域,我们可以通过Archetype 快速访问到所有该类型的Entity 数据。同时,当我们对一个实体的组件进行添加、删除操作时,就会修改这个实体的原型,ECS 会将其移动到其他存储区域。
-
Chunk(内存块): 书接上文,Chunk 与Archetype 有着密不可分的联系。ECS底层将同一Archetype 的数据存储在一系列的内存块中,数据增加时就会不断分配新的Chunk。这种底层机制说明,当我们查找某一类具有相同性质的实体时,只需要根据原型进行查找,而不是在所有的Entities 中进行遍历。
其实,不管是Archetype or Chunk, 更多的是概念性的东西,我们在实际开发中并不会经常接触到。
总结
通过了解ECS中的一些概念,我们已经对ECS有了一个大体的认识。ECS相较于传统的项目架构,可以说上手难度+++,并且脚本数量也会+++,当你真正上手的时候就会发现,原来写在一个MonoBehavior 中的代码,转到ECS 后,竟然可以写成好几个甚至十好几个Scripts, Amazing(No)!
ECS的好处也是显而易见的,通过特殊的低层机制,当我们需要对一组大量对象进行相似的处理时,ECS可以大幅提高代码速度、内存访问速度,从而使程序的帧数有明显的提升。
但是(还有转折!),在你决定上手ECS之前要先想这样一个问题:ECS能够对我的项目起到多大的提升作用?能够抵消团队的学习开发和维护成本吗?写出来的代码可读性怎么样,能够让新人快速上手吗(并不能)?
如你所见,ECS对于需要处理大量相似实体的程序有明显的提升:比如你想实现一个大型的人群仿真项目,其中每个人的行为逻辑都是相同或相似的;但是当你的程序更偏向于ARPG游戏(每个NPC对象的处理逻辑完全不同),那么ECS可能对你的程序性能并不会有多大的改善。
题外话:关于DOTS
说到ECS,就要提一下与之密切相关的另一个东西–DOTS, 当然它其实不是一个东西,而是一套集合型的高效技术堆栈,那么DOTS包含什么呢?
Job System | 编写多线程代码 |
ECS | 编写高性能代码 |
Burst Compiler | 编译生成高性能代码 |
总的来说,DOTS本身就包含了ECS!但是ECS会涉及到DOTS 的核心内容以及大部分用法,因此关于Job System 和Burst Compiler 本文不会过多介绍(因为我也不会啦)。
其他
贴上一些ECS好文,对新手来说也非常友好了
- Unity ECS 基础概念介绍 风小锐
- Unity DOTS:入门简介 烟雨迷离半世殇
- 木头哥的ECS教程,入门啃官方Demo推荐
- 官方Demo全解析+深入学习 CloudHu1989
- ECS由浅入深 没有分专栏,需要从历史文章中找 王王王渣渣
本文为原创,如有错误欢迎指正,感谢阅读。
未完待续…