ECS 入门到入土: 一、什么是 ECS

编写本文时,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

ECS 入门到入土: 一、什么是 ECS

  • 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 的概念我们会在后面说道。

ECS 入门到入土: 一、什么是 ECS

以该图为例,System 读取Entity A 和 B 的(Translation–位置, Rotation–角度)组件数据,并且进行计算,最后对两个实体的LocalToWorld 进行数据的更新。

这里就体现了ECS 的与众不同,具有相同的组件的实体会被放置在相同或相邻的内存块,使得System 能够对同一种类型的实体进行快速的查找,并且能够实现并行的数据处理,从而提高了程序的执行速度。

此处可以直接参考官方文档(English): WorldEntityComponentSystem

Chunk Archetype

  • Archetype(原型): 一系列组件的唯一组合称为原型Archetype,换言之拥有完全相同的组件类(数据可以不相同)型的两个实体属于同一种原型。

    Archetype 所对应的Entity Component数据都存储在同一片区域,我们可以通过Archetype 快速访问到所有该类型的Entity 数据。同时,当我们对一个实体的组件进行添加、删除操作时,就会修改这个实体的原型,ECS 会将其移动到其他存储区域。
    ECS 入门到入土: 一、什么是 ECS

  • Chunk(内存块): 书接上文,Chunk 与Archetype 有着密不可分的联系。ECS底层将同一Archetype 的数据存储在一系列的内存块中,数据增加时就会不断分配新的Chunk。这种底层机制说明,当我们查找某一类具有相同性质的实体时,只需要根据原型进行查找,而不是在所有的Entities 中进行遍历。
    ECS 入门到入土: 一、什么是 ECS

其实,不管是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好文,对新手来说也非常友好了

本文为原创,如有错误欢迎指正,感谢阅读。
未完待续…

上一篇:EF和EFCore


下一篇:ASP.NET Core 中的 ORM 之 Entity Framework