Unity DOTS 学习笔记1 - ECS 0.50介绍和安装
为什么学习这个技术
ECS的全称为Entity Component System,是最早由暴雪在GDC2017上提出的一个新的游戏设计框架,数据和处理上都和目前主流的面向对象OOP有很大的差距。他是面向数据编程(DOD),他的优点也就是缺点,不用繁杂的对象和继承关系,不是面向对象思想,他是面向数据,以处理符合条件的数据来编程的思想,他的最大优点就是快。
看下图说明:来源
https://learn.unity.com/tutorial/part-1-understand-data-oriented-design?uv=2020.1&courseId=60132919edbc2a56f9d439c3#
在 OOP 中,代码遍历 Sphere 类数组以检查每个类的颜色并设置绿色类的位置。虽然数组是打包的连续数据,但它只包含对 Sphere 类的引用,实际数据可能分散在整个内存中,导致缓存未命中。在 DOD 中,球体被分解为颜色和位置组件并打包到缓冲区中,从而减少缓存未命中并加快处理速度。
下表是大概的内存和缓存(Cache)速度对比。
ECS原理就是提高Cache命中率,把需要计算的数据全部放入Cache,减少内存和Cache交换数据,达到速度提升。
要成功地使用 DOD,您需要忽略许多 OOP 概念,这些概念似乎绝对是编程的基本方面。忘记封装和数据隐藏。忘记继承和多态。在很大程度上,忘记引用类型。这些概念对你没有帮助。
Unity ECS简介
从ECS刚开始就开始关注这个技术了,每次版本的升级,总是有代码的变动,要修改很多代码,间断的学习,总是会忘记很多,所以才有了这个学习笔记,也算是一种记录吧。ECS整体版本现在已经到了0.50.0版本了,所以后面我们的笔记都是基于这个版本。
ECS全称是 Unity’s Entity Component System (ECS). 也是 Unity Data-Oriented Tech Stack(DOTS)面向数据栈编程的核心。
Entities 实体:填充您的游戏或程序的实体或事物。可以理解为承载Component的载体。
Components 组件:与您的实体相关联的数据,但由数据本身而非实体组织。这种组织上的差异是面向对象设计和面向数据设计之间的主要区别之一。
Systems 系统:将组件数据从当前状态转换为下一个状态的逻辑。例如,一个系统可能会更新所有移动实体的位置,它们的速度乘以自上一帧以来的时间间隔。
具体的还可以通过这篇文章来了解:
https://learn.unity.com/tutorial/shi-yao-shi-dots-wei-shi-yao-shuo-dotsfei-chang-zhong-yao#
ECS的安装
升级:老版本的升级注意事项,大家可以参考官方
https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/upgrade-guide.html
这里不多说明。
ECS需要安装的包
“com.unity.entities”: “0.50.0-preview.24”,
“com.unity.rendering.hybrid”: “0.50.0-preview.24”,
“com.unity.physics”: “0.50.0-preview.24”,
“com.unity.burst”: “1.7.0”,
“com.unity.collections”: “1.1.0”,
“com.unity.jobs”: “0.50.0-preview.8”,
“com.unity.mathematics”: “1.2.5”,
“com.unity.platforms”: “0.50.0-preview.4”,
默认安装
com.unity.entities
com.unity.rendering.hybrid
com.unity.physics
这三个包,会自动安装
“com.unity.collections”: “1.1.0”,
“com.unity.jobs”: “0.50.0-preview.8”,
“com.unity.mathematics”: “1.2.5”,
如果这几个没有安装,请手动安装。
可以再PackageManager里点击加号通过git URL安装,输入包名就可以了,例如com.unity.entities
常见问题
因为安装这里遇到了不少问题,所以要讲以下:
1,首先要安装Unity2020.3.30+以上的版本,注意这里的小版本3.30,至少得是3.30,因为新的ECS需要新的Editor支持。否则安装了ECS后会有报错。
2,ECS的Hybrid,从0.50开始只支持V2版本了,需要通用渲染管线或者高清渲染管线的支持URP9.0,所以你的项目需要创建选择一种,或者没有安装的话,通过PackageManager后面来安装,具体如何配置渲染管线,可以参考其他文章,这里不做介绍。
另外Hybrid不支持桌面OpenGL或者GLES。对于Android和Linux,目前只能使用Vulkan。在ProjectSetting的Player的Graphics APIs中进行配置。
另外Color Space改为Linear,不支持gamma,所以在项目设置中要改以下。
在URP的设置中需要勾选Advanced SRP Batcher
具体要求参考下面文档:
https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@0.50/manual/requirements-and-compatibility.html
https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@0.50/manual/creating-a-new-hybrid-renderer-project.html
3,Build ECS项目,必须安装com.unity.platforms,不支持通过BuildSetting的Build操作。要构建需要生成构建配置。menu: Assets > Create > Build Configuration > Empty Build Configuration.
以上就是安装和一些配置的注意事项。
Unity ECS 系统包含了哪些
ECS概念
实体组件系统 (ECS) 架构将身份(实体)、数据(组件)和行为(系统)分开。该架构专注于数据。系统读取组件数据流,然后将数据从输入状态转换为输出状态,然后实体对其进行索引。
Archetypes 原型
组件类型的唯一组合称为EntityArchetype。例如,一个 3D 对象可能有一个用于其世界变换的组件,一个用于其线性运动,一个用于旋转,一个用于其视觉表示。这些 3D 对象的每个实例对应于一个实体,但由于它们共享相同的组件集,ECS 将它们分类为单个原型:
在此图中,实体 A 和 B 共享原型 M,而实体 C 具有原型 N。
为了平滑地更改实体的原型,您可以在运行时添加或删除组件。例如,如果您Renderer从实体 B 中移除组件,则它会移动到原型 N。
你可以通过Window > DOTS > Archetypes.来观看当前的Archetypes
内存块 Memory Chunks
实体的原型决定了 ECS 存储该实体组件的位置。ECS 以“块”的形式分配内存,每个块都由一个ArchetypeChunk对象表示。块总是包含单一原型的实体。当一块内存变满时,ECS 会为使用相同原型创建的任何新实体分配一块新的内存。如果您添加或删除组件,然后更改实体原型,ECS 会将该实体的组件移动到不同的块。
这种组织方案在原型和块之间提供了一对多的关系。这也意味着找到具有给定组件集的所有实体只需要搜索现有的原型,这些原型通常数量很少,而不是所有的实体,它们的数量通常要大得多。
ECS 不会按特定顺序存储块中的实体。当实体被创建或更改为新原型时,ECS 将其放入存储原型的第一个块中,并且该块具有空间。然而,大块仍然紧紧地包裹着。当一个实体从原型中移除时,ECS 将块中最后一个实体的组件移动到组件阵列中新腾出的插槽中。
实体查询 Entity queries
要确定系统应处理哪些实体,请使用EntityQuery。实体查询在现有原型中搜索具有符合您要求的组件的原型。您可以使用查询指定以下组件要求:
All — 原型必须包含All类别中的所有组件类型。
Any — 原型必须至少包含Any类别中的一种组件类型。
None— 原型不得包含无类别中的任何组件类型。
实体查询提供包含查询所需组件类型的块列表。然后,您可以直接使用IJobEntityBatch遍历这些块中的组件。
例如下面的查询:
EntityQuery query = GetEntityQuery(typeof(Rotation),
ComponentType.ReadOnly<RotationSpeed>());
Job 工作
要利用多线程,您可以使用C# Job system。ECS 提供SystemBase类Entities.ForEach以及IJobEntityBatch Schedule()和ScheduleParallel()方法,用于在主线程外转换数据。Entities.ForEach是最简单的使用,通常需要更少的代码行来实现。您可以将 IJobChunk 用于Entities.ForEach无法处理的更复杂的情况。
ECS按照您的系统排列顺序在主线程上安排作业。随着作业的调度,ECS 会跟踪哪些作业读取和写入哪些组件。读取组件的作业取决于写入同一组件的任何先前计划的作业,反之亦然。作业调度器使用作业依赖关系来确定哪些作业可以并行运行,哪些必须按顺序运行。
System organization 系统组织
ECS 按World组织系统,然后按group组织系统。默认情况下,ECS 会创建一个包含一组预定义组的默认 World。它找到所有可用的系统,实例化它们,并将它们添加到默认 World 中的预定义模拟组中。
您可以指定同一组内系统的更新顺序。组是一种系统,因此您可以像任何其他系统一样将组添加到另一个组并指定其顺序。组内的所有系统在下一个系统或组之前更新。如果您不指定顺序,ECS 会以不依赖于创建顺序的确定方式将系统插入到更新顺序中。换句话说,即使您没有明确指定顺序,同一组系统在其组内也始终以相同的顺序更新。
系统更新发生在主线程上。但是,系统可以使用作业将工作卸载到其他线程。SystemBase提供了一种直接的方式来创建和调度作业。
有关系统创建、更新顺序以及可用于组织系统的属性的更多信息,请参阅系统更新顺序文档。
下面的图大概就可以看出World和Group的关系,可以通过Window > Analysis> Entity Debuger开启。
ECS authoring 创作
当您在 Unity 编辑器中创建游戏或应用程序时,您可以使用 GameObjects 和 MonoBehaviours 创建一个转换系统,将这些 UnityEngine 对象和组件映射到实体。
经是通过传统的Unity对象经过转换系统,进行变换成ECS系统可以识别的对象。
后面再具体介绍。