DOTS(2) DOTS中的ECS架构与概念术语

  1. 1. Entity-Component-System
    1. 1.0.1. 实体组件系统(ECS)架构
    2. 1.0.2. 游戏引擎架构演变
    3. 1.0.3. DOTS1.0中的ECS
    4. 1.0.4. DOTS下关于ECS的专有名词和概念
    5. 1.0.5. Entities and Components术语
    6. 1.0.6. System术语
    7. 1.0.7. DOTS下的ECS
    8. 1.0.8. 补充

 

Entity-Component-System

实体组件系统(ECS)架构

  • 遵循组合优于继承的原则
  • 面向数据设计
  • 弱耦合
  • 常被应用在游戏开发上

游戏引擎架构演变

  • 在ECS架构出现之前,早期的游戏引擎或游戏的架构,多以多层对象继承的方式来组织,都是按空间节点为较基础的对象,一层一层抽象继承下来的
  • 后来随着发展,游戏引擎逐渐演变成以Actor Component这种以轻继承,以对象组合代替多层继承的方式来组织
  • 其中Actor是个Component的容器,而负责具体逻辑更新部分则由具体的Component负责
  • 对象几乎没有多态,做到了对象间的解耦
  • 如Reality Engine、UE3以后的版本以及Unity几乎都是这种架构
  • 我们要明确ECS架构的本质,组合的是数据数组,而非对象数组。其中Entity本身虽然叫做实体,但并非对象,也不是一个容器,而是一个对象索引的id,是一个标识符,其中并不包含任何数据与逻辑;而Component才是个容器,但它不是一个对象容器,而仅是一个数据容器,其中不包含任何逻辑。
  • Entity与Component的关系,往往是Entity充当数据的标识符或Key
    来使用,而ECS中的S(System)才是真正负责对数据进行操作的部分
  • 换句话说,System才是对具有特定组件的特定实体执行的操作

DOTS1.0中的ECS

  • ECS架构一直在演变
  • 应用场景不同,各家实现和工作流有差异
  • DOTS1.0中的ECS与老版本变化较大,不要看老资料
  • DOTS1.0中,为了让大家从面向对象设计向面向数据设计编写代码时,过度更平滑些,引入了不少特性与专有名词,让大家在编写代码时能找到一些面向对象的感觉。因此我们在学习DOTS时,不要先入为主去质疑DOTS下的ECS为什么如此设计,要以DOTS下的ECS工作流方式去学习。另外DOTS中的ECS只是DOTS中的一部分,并不是你了解ECS架构,甚至自己动手实践过ECS就一定能学好
  • 开发心态去接受

DOTS下关于ECS的专有名词和概念

  • Archetypes原型Archetypes Chunk的概念

    • Archetypes依然不是个对象,它依然是一个标识符,它标识的是所有具有相同Component组合的实体类型
    • 举个例子,这里由Position、Rotation、Renderer、Rigid Body四个Component组件组合成的Entity A与Entity B共享一种原型,而由Position、Rotation、Renderer三个组件组合成的Entity C则属于另外一种原型
    • 这里可以将Archetype原型理解成不同的Entity在内存上的Layout布局
    • 如图所示,26个Entity被分为8种原型,而每个Archetype原型所标记的内存会被分成固定大小连续的非托管内存块,被称为Archetype Chunk,这里简称Chunk
    • Chunk中会包含共享同一原型的实体组件数组,默认设置下每个Chunk大小为16KB,如果实体组件填充不满的情况下,也要有留白
    • Chunk存在的目的,是为了方便做数据并行计算,方便做缓存的prefetch在数据对齐的同时又可以匹配缓存的catch line,另外在Query查询时,无论是使用主线程查询、工作线程并行查询、还是按Chunk查询时,都可以利用其在Component的数组纵向维度上分块对齐的方式来做slice切片
  • WorldEntityManager的概念

    • World是一系列Entity的组合,每一个Entity在一个World中是唯一的,统一受到world中的EntityManager结构的管理
    • EntityManager负责创建、销毁、修改世界中的实体
  • Structural Change的概念

    • 所有导致需要重新组织内存块或内存块内容的操作都称为Structural Change
    • 这里包括两个重点,一个是改变结构,一个是改变内容,这两种改变都只能在主线程中做,而不能在工作线程中做,是Resource Intensity资源密集类型的操作,效率很差
    • Structural Change
    • 应该能想到,添加或删除一个Entity对应的组件,导致所属原型发生变化,就属于Structural Change的概念
    • 另外,创建和销毁Entity实体、设置Share Component值也会被视为Structural Change,要尽量避免
    • 而当我们明确我们一定不会有Structural Change的操作时,我们可以在编辑时做Bake的操作,虽然会降低运行时的逻辑灵活性,但会提高运行时效率

  • 这张图可能会造成误解
  • 不同原型内的相同组件,在内存上并不是像上图这样是连续的,这只是为了展示在Query查询时,会通过slice切片方式来阻止查询,让你感觉上是连续的,但并不会造成内存上的移动

Entities and Components术语




System术语

DOTS下的ECS

  • Entity : Entity不是一个容器,它只是一个标识符,它用来指示某个对象的存在。系统可以用它来操作,可以通过组件来分配某些属性
  • Component : 组件是一个数据容器,没有任何逻辑,一组特定的参数关联在一起并定义属性
  • System : 系统是负责对数据进行操作的部分。换句话说,系统是对具有特定属性(组件)的特定实体执行的操作

补充

  • 类比例子并不是为了说明缓存分级的原因
  • 而是用组织排队去理解面向数据设计,可以更形象的理解ECS、Jobs与Burst在DOTS中分别扮演哪些角色,做了哪些事
  • 缓存分层的原因本质是物理距离决定的