Dots 概述

什么是Dots

  • Data Oriented Technology Stack 面向数据的技术栈
  • The C# Jobs:多线程控制

  • The Burst compiler:更快速的编译器

  • Unity Mathematics

  • Collections

  • Entities:数据和模组分开

为什么要用Dots

Unity 传统方法

  • 面向对象的方法原本数据在内存中随机分布,每次需要调用一个组件或者方法的时候在内存上找毫无规律,查找效率慢
  • 只有一个主线程,虽然有协程,实际上也是在主线程上控制的
  • GameObject 在使用时,在调用其某些属性时,其他属性也还在 GameObject 上,浪费内存空间

Unity Dots 特点

  • 与面向对象的设计相比,Dots面向数据和内存,可以多线程能够更好的应用到多核CPU
  • ECS 的数据是有规律存放,在找的时候可以很快找到并且更快找到其类似的数据,提高了效率,有利于批量处理
  • Data 和 Component 分开,在不需要某些 Component 时可以关闭
  • 在大型开放世界,多人场景,需要多线程加载的情况效果最好

怎么用Dots

  • ECS

ECS 概述

  • Entity
  • Compoment
  • System

Entity

托管对象(Managed objects):零散随机分布在内存中,使用CG机制,创建和销毁都需要很大的消耗,例如 GameObject,这类对象不可以用在 Jobs 也不接受 Burst 编译

非托管对象(UnManaged objects):Entity

  • Entity 实际上是不包含数据的一个抽象的对象,是很多组件集合在一起的

    • 每一个Entity 具有在 World 唯一的 Index,是这个 Index 关联的一系列 Component 和 Data 的特别组合,Component 和 Data 是对应的
    • Entity 的另一个标识符是 Version,每次 Index 被回收的时候,版本就更新,保证在Entity被回收后不会引用同一个 Index
  • World 是 Entity 的集合,不同世界的 Entity 可能会有相同的 Index

  • EntityManager 管理世界所有 Entities
  • Entity 的查询实际上是找到特定组合的 Archetype

Entity 在内存的存储

Chuck

UnManagered Memory:非托管内存,不由GC控制,需要手动分配释放

  • 一块 UnManagered Memory 16Kb 就是一个 Chunk,它包含组件的实际数值
  • 包含数据的 Chunk 会交替放到 CPU 中,这样可以快速遍历到需要的数据,不需要去 RAM 里面随机找,速度更快
  • 每块 Chunk 里面有 n 个 Entity 相同 Component 组合的数据

    • 在一个 Chunk 里 Entity 的属性越多,存放的 Entity 的数量越少
  • 假设一个 Chunk 里面有3个实体,每一种属性是一个数组,那么这里4个数组的相同位置对应的就是同一个 Entity

image-20230331183633856

数据结构变化

  • 原始的数据结构,引用分布在内存随机的位置,查找效率低

image-20230331183050285

  • 面向内存的数据结构,相同的数据相邻,查找效率高

image-20230331183138347

Archetype

  • Archetype 原型是 Chunk 中组件结构相同的标识符,方便数据并行计算;即每个 Chunk 保存的都是同一种 Archetype
  • 增删组件会导致重新组织内存块,原型发生变化消耗大

image-20230328231828710


Component

  • 组件,不包含数据,使用 Archetype 保存

System

  • 每一个 System 实例属于一个世界
  • System 是一个层级结构,具有 Child SystemChild System Groups
    • Initialization:Initialization System Group
    • Update:Simulation System Group
    • PreLate Update:Presentation System Group

SystemBase

  • 在主线程的托管类,不能被 Burst 编译

ISystem

  • 非托管类,可以被 Burst 编译

Jobs

  • 多线程并发处理
  • 需要注意线程之间的依赖关系
Run() Schedule ScheduleParallel
主线程 子线程调度 平行调度

一个简单的流程

例如:NPC向前走

安装

  • Unity 2022.2.0b16 + com.unity.entities 1.0.0-exp8

新建子场景(World)

  • 创建 SubScene,也就是一个 World

创建Entity

IComponent

  • 为 GameObject 创建向前走需要的速度变量
1
2
3
4
public struct NPCMoveProperties : IComponentData
{
public float Speed;
}

Baker

  • 创建自定义数据集,将 GameObject 转换为 Entity,也就是将 Authoring 下的数据转换为 Runtime 的数据,可以在 Inspector 窗口上对于 GameObject 赋值
1
2
3
4
5
6
7
8
9
10
11
12
public class NPCMono : MonoBehaviour
{
public float Speed;
}

public class NPCBaker : Baker<NPCMono>
{
public override void Bake(NPCMono authoring)
{
AddComponent(new NPCMoveProperties { Speed = authoring.RiseRate });
}
}

Aspect

  • 将 ICompoment 封装在一起,方便 System 调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public readonly partial struct NPCAspect: IAspect
{
public readonly Entity Entity;

private readonly TransformAspect _transformAspect;
private readonly RefRO<NPCMoveProperties> _NPCMoveProperties;

public float Speed => _NPCMoveProperties.ValueRO.Speed;

public void Walk(float deltaTime)
{
_transform.Position += _transform.Forward * Speed * deltaTime;
}
}

System 运行(Job)

  • 在子线程上进行调度,多个NPC行走是并行任务
1
2
3
4
5
6
7
8
9
10
11
12
[BurstCompile]
public partial struct NPCWalkJob: IJobEntity
{
public float DeltaTime;
public EntityCommandBuffer.ParallelWriter ECB;

[BurstCompile]
private void Execute(NPCAspect npcAspect, [EntityInQueryIndex] int sortKey)
{
npcAspect.Walk(DeltaTime);
}
}
  • 在System上运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[BurstCompile]
// [UpdateAfter(typeof(NPCSpawnSystem))]
public partial struct NPCWalkSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
}

[BurstCompile]
public void OnDestroy(ref SystemState state)
{
}

[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var deltaTime = SystemAPI.Time.DeltaTime;
var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();

new NPCWalkJob
{
DeltaTime = deltaTime,
ECB = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter()
}.ScheduleParallel();
}
}

扩展

Unity Dots 官方 Sample

Unity Dots API

Unity ECS 1.0 Full Project Tutorial | Step-by-Step

Unity DOTS 1.0 in 60 MINUTES!

动画

  • unity.entity.animation 目前不适配 Entity 1.0版本,没有官方的动画处理
  • 开源的 dmotion 适用于 Entity 1.0版本,但其依赖的 Latios-Framework 不支持 Transform V2,导致 LocalTransform 等结构体无法使用