ECS的核心概念

作者: 洪智 | 来源:发表于2020-09-20 18:21 被阅读0次

洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智探索DOTS。

通过前几节的学习咱们已经知道了ECS包含三个部分:实体(entities)、数据(components)、行为(system)。ECS架构的核心是数据,这也是为什么Unity会将这一套技术栈命名为DOTS。System会读取entity上面的component的数据流,并处理数据。实体在这里其实更像是索引,它本身并不包含任何数据和逻辑。

具体看下图:

这个图中,System读取了多个实体的TranslationRotation组件,然后经过计算处理,将结果更新到LocalToWorld组件中。

从图中你可以看到,实体A和B还有Renderer组件,但是C并没有。不过这并不会影响System的计算逻辑,因为这个系统不关心Renderer组件。

你还可以写一个系统,需要处理Renderer组件,这样系统就会忽略实体C。你还可以写一个系统排除包含Renderer组件的实体,这样系统就会忽略实体A和B。

下面对ECS中比较重要的几个核心概念做一个梳理:

原型 Archetypes

多个组件的组合叫做一个原型。

比如一个3D物体可能会包含用于transform的组件,包括移动、旋转、渲染,每个3D物体对应一个实体,但是他们都有同样的组件,所以ECS会把他们分类成是一类原型。

在上图中,实体A和B的原型都是M,实体C的原型是N。

你也可以通过在运行时添加或者移除component来改变一个实体的原型。例如:如果将实体B的Renderer组件移除,实体B的原型就会变成N。

内存块 Memory Chunks

为什么要先讲原型的概念呢,因为一个实体的原型是什么,决定了ECS会将实体的components也就是数据存在什么地方。ECS按块分配内存,每块用一个ArchetypeChunk对象表示。

一个块只包含一种原型,可以包含的多个实体的数据。如果一个块的内存满了,ECS会分配一个新的块来存储新的实体的components。

如果你修改了实体的组件,那就相当于修改了实体的原型,这时候ECS会将实体的组件数据移到另外一个块中。

原型和内存块的关系是一对多的关系。这就意味着,如果想查询给定的一组component类型的所有实体,只需要在这些原型中搜索即可。这样会比在所有的实体中查找效率高很多。

ECS在存储实体到内存块中没有特殊的排序,当创建一个实体或者实体的原型发生变化时,ECS会将它放到对应原型的第一个还有空间的内存块中。内存块中的数据会紧密排列。如果一个实体要被移出当前原型的内存块,这时候会有个空位,ECS会把这个内存块最后的实体数据移动到这个空位中。

注意:原型中的共享组件(后面会具体说这是个什么东东)的数据也会影响实体会被存在哪个内存块。同一个内存块中的所有实体的共享组件中的数据值都是相同的。如果你修改了共享组件中的数据,这个实体会被移到另外一个块中,有点类似修改了实体的原型。

将共享组件的实体分到一个内存块中会提高处理他们的速度。比如Hybird Renderer(混合渲染)定义了RenderMesh组件来达成这个目的。

实体查询

一个System根据什么来决定处理哪些实体呢?这时候会用到一个叫实体查询(Entity Query)的东西。实体查询首先需要一些组件类型,然后根据你传入的组件类型的组合,在包含这些组件的原型中查询符合要求的实体。查询时可以指定下面三种类型:

  • All 必须包含All中所有的组件类型
  • Any 必须包含Any中至少一个组件类型
  • None 不能包含None中任意一个组件类型

一次实体查询的结果会返回所有符合查询要求的内存块,你可以使用IJobChunk来迭代遍历所有的组件。(IJobChunk后面会讲。)

Jobs 作业

之前说过,ECS配合Job使用才能发挥多线程的威力。ECS提供了SystemBase类,其中包含Entities.ForEach方法,还包含了IJobChunkSchedule()ScheduleParallel()方法,可以在子线程中处理数据。Entities.ForEach是最简单的方法,只需要几行代码就能实现。IJobChunk可以用来处理比较复杂的情况。

ECS会在主线程调度Job,根据System的顺序。当job调度后,ECS会追踪哪些job在读写哪些组件。需要读权限的job需要等待前面写权限的job执行完,反之亦然。Job调度器会使用job依赖来决定哪些job可以并行,哪些必须串行。

System的组织

ECS通过Worldgroup来组织system。默认情况下,ECS会创建一个默认的World,包含一些预定义的group组。它会找到工程中所有的System,实例化他们,并添加到预定义的group中。

你可以指定同一个group中system的Update的执行顺序。Group也是一种system,所以你可以将一个group添加到另外一个group中。如果你没有指定顺序,system的执行顺序会不太确定,并不会按照它们创建的顺序。不过,同一个group中的所有system都会比下一个group中的system先执行。

System的Update是在主线程中执行的,不过可以使用Job将工作分配到子线程中。

ECS世界的可视化创建(authoring)

在Unity中没办法直接可视化地创建DOTS世界,但是,可以先用GameOjbect和MonoBehaviour来创建,然后通过转换系统将GameObject转换成实体和组件。这个我们后面也会细讲。

总结

这节讲述了ECS中多个核心概念,到这,你可能对上一节的代码就有更深入的理解了。

扩展阅读

【扩展学习】洪流学堂公众号回复DOTS可以阅读本系列所有文章,更有视频教程等着你!


呼~ 今天小新絮絮叨叨的真是够够的了。没讲清楚的地方欢迎评论,咱们一起探索。

我是大智(微信:zhz11235),你的技术探路者,下次见!

别走!点赞收藏哦!

好,你可以走了。

相关文章

  • ECS的核心概念

    洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智探索DOTS。 通过前几节的...

  • ECS初试

    ECS初试 最简单的ECS尝试 ECS最简单的概念就是Entity实体对象挂载Component组件,组件中包含纯...

  • 弹性计算服务ECS基本概念——实例

    ECS的产品概念 ECS,是由多个并列,又互相关联的产品概念组成,包括 实例,instance 磁盘,disk 快...

  • Unity ECS 入门

    本文主要总结Hybrid ECS基础用法,Pure ECS将会在之后进行补充。不详细描述ECS概念与原理。更多内容...

  • Unity下一轮最大的变革-Entity Component S

    新一代Entity Component System(ECS)将会彻底改变Unity的底层概念(GameObjec...

  • JS(执行上下文、函数堆栈、提升)

    一、几个概念 EC(Execution Context):函数执行环境(或执行上下文) ECS(Execution...

  • 核心概念

    文件格式 文件格式纯文本格式是Robot Framework中最常用的一种数据格式。使用两个或者更多的空格来分隔单...

  • 核心概念

    React 是JavaScript 库,可以快速构建快速响应的大型WEB应用程序。 JSX JSX是一种 J...

  • 进阶学习6-装饰器/迭代器/生成器

    总结 描述备注函数核心概念1函数可以赋值给变量函数核心概念2函数可以作为参数传递函数核心概念3嵌套函数函数核心概念...

  • 关于大概念

    研究大概念案例注意三点: 一、找到内容的核心概念和核心问题 二、理清围绕核心概念和核心问题如何拓展迁移 三、构建大...

网友评论

    本文标题:ECS的核心概念

    本文链接:https://www.haomeiwen.com/subject/vnhpyktx.html