DOTS1.0发布了,记录一下它的简单使用。

笔记基于:Unity DOTS 1.0 in 60 MINUTES! - YouTubeSpawner example | Entities | 1.0.0-pre.44 (unity3d.com)

相关链接:Unity ECS 1.0 Full Project Tutorial | Step-by-Step 🧟‍♂️ - YouTube

官方的例子:Unity-Technologies/EntityComponentSystemSamples (github.com)

准备

Unity需要2022版本,新建一个3D URP项目,打开Package Manager窗口,点击左上角的加号,再点击“add package by name”,输入“com.unity.entities”回车,等待Unity自动安装。在写此笔记时,DOTS虽然已经是1.0,但还是preview(记住打开Package Manager的允许预览包)。所有的依赖也会安装。

可以在窗口中看到所有的依赖

再次重复步骤,输入“com.unity.entities.graphics”,安装基于DOTS的图形包,这个包能够基于DOTS渲染Mesh,并且支持实例化网格渲染和LOD。

Entities使用微软的源生成器来生成代码,所以需要使用Visual Studio 2022或者Rider2021.3.3以上版本。

在Project Settings——Editor窗口,找到“Enter Play Mode Settings”,勾选“Enter Play Mode Options”,但是不要勾选“Reload Domain”和“Reload Scene”。这会关闭Unity的“域重载”,但是关闭“域重载”后静态变量和静态事件函数不会重置,有关解决方法请参考:Unity Domain Reoad

创建Entities

创建SubScene

在Hierarchy面板右键——New SubScene——Empty Scene,在弹出的窗口中输入场景名并保存。

创建SubScene

SubScene的作用就是将在其内部创建的GameObject转化为Entities。

创建SubScene的GameObject

点击选中刚刚创建的SubScene,右键——GameObject——Create Empty,使用它的默认名字“GameObject”。

选中这个GameObject,在它的Inspector右上角有一个小圆圈,点击小圆圈,会弹出几种Entity的查看方式,我们选择Runtime,这种方式会显示此Entity包含的所有信息。

Entity

选中“GameObject”,右键——3D Object——Capsule。选中Capsule,删除它的Capsule Collider。

点击Window——Entities——Entities Hierarchy,这个Hierarchy可以显示用于ECS的所有项目。它的右上角也有小圆圈,点击切换为Runtime,然后在这个窗口中点击所有Entities项目,都是直接转换为Runtime模式。

Entities Hierarchy

创建Component

在Project窗口中新建Scripts文件夹,在其中创建一个脚本命名为“Speed”。

1
2
3
4
5
6
using Unity.Entities;

public struct Speed : IComponentData
{
public float value;
}

Component作为数据容器,需要与Entity联系,还需要一定的方法和GameObject联系,用来方便地设置各种数据。

再新建一个脚本,命名为“SpeedAuthoring”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using UnityEngine;
using Unity.Entities;

public class SpeedAuthoring : MonoBehaviour
{
public float value;
}

public class SpeedBaker : Baker<SpeedAuthoring>
{
public override void Bake(SpeedAuthoring authoring)
{
AddComponent(new Speed() { value= authoring.value });
}
}

Unity Entities提供了Baker类,用于将GameObject数据转化为ECS使用的ComponentData。

将“SpeedAuthoring”挂载到我们之前在EntitySubScene创建的GameObject,随便设置个数字。并打开Runtime

Speed Component

可以看到Speed数值已经顺利转化。

创建System

SystemBase和ISystem

System 区别
SystemBase 1. Class-Managed 2. 只能在主线程运行 3. 不能使用Burst编译器
ISystem 1. Struce-Unmanaged 2.可以使用Burst编译器 3. 多线程

一些非常基础的代码,或者只希望在主线程运行的代码,可以使用SystemBase

为了更高的性能,使用ISystem

使用SystemBase

在Scripts文件夹内新建脚本,命名为“MovingSystemBase”

1
2
3
4
5
6
7
8
9
using Unity.Entities;

public partial class MovingSystemBase : SystemBase//partial
{
protected override void OnUpdate()
{

}
}

继承SystemBase必须使用partial关键字,用来优化DOTS的编译

如果我们想使用System移动物体,就需要这个物体Entity对应的World Transform、Local Transform等默认的Component

Entity

为了简化Component操作,Unity在DOTS1.0提出了Aspect概念:

Aspect overview | Entities | 1.0.0-pre.15 (unity3d.com)

Aspect是一个类似Object的包装器,您可以使用它将实体组件的子集组合到一个C#结构中。Aspect对于组织Component代码和简化系统中的Query非常有用。Aspect也能添加一些函数。

Aspects

默认的Transform Aspect可以将具有父对象的Entity的相对于父级的坐标和相对于世界的坐标同步。更方便地移动entity。

我们修改“MovingSystemBase”脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using Unity.Entities;
using Unity.Transforms;//TransformAspect继承自它
using Unity.Mathematics;//使用Mathematics来计算

public partial class MovingSystemBase : SystemBase
{
protected override void OnUpdate()
{
foreach (var transformAspect in SystemAPI.Query<TransformAspect>())//SystemAPI.Query是Entities提供的Component查询函数,当然也可以查询Aspect,它能够查询所有支持的Component
{
transformAspect.LocalPosition += new float3(SystemAPI.Time.DeltaTime, 0, 0);
//另外一个可选的移动方法,使用TransformAspect封装好的函数
//transformAspect.TranslateWorld(new float3(SystemAPI.Time.DeltaTime, 0, 0));
}

//下面是Entities.ForEach的查询方法,这个方法返回一个Job,要调用Schedule(工作线程)。注意这里的Lambda写法,要提前指定好参数类型
//Entities.ForEach( (TransformAspect transformAspect) =>
//{
// transformAspect.LocalPosition += new float3(SystemAPI.Time.DeltaTime, 0, 0);
//}).Schedule();
}
}

在C# JobSystem里面,Run表示在主线程运行,Schedule表示在一个工作线程中运行,ScheduleParallel表示在多个工作线程中运行

使用Entities.ForEach来查询有一定的缺点,就是不能嵌套轮询。而使用foreach可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;

public partial class MovingSystemBase : SystemBase
{
protected override void OnUpdate()
{
foreach (var transformAspect in SystemAPI.Query<TransformAspect>())
{
foreach(var speed in SystemAPI.Query<Speed>()){

}
transformAspect.LocalPosition += new float3(SystemAPI.Time.DeltaTime, 0, 0);

}
}
}

这时我们进入运行模式,会看到所有的Entities会移动。这时我们打开WIndows——Entities——Systems,在Update——Simulation System Group中可以看到我们的Moving System Base

Moving System Base

点击左侧的“小插头”图标,会停止这个System的功能