Unity DOTS简明教程
什么是DOTS
首先,先来了解下什么是DOTS?
DOTS是Data-Oriented-Tech-Stack,官方中文翻译是:多线程式数据导向型技术堆栈。
它主要由三部分组成:
- 任务系统
- 实体组件系统
- Burst编译器
下图是Unite2019的介绍截图:
所以,DOTS是一套集合型的高效技术堆栈,整合了Job System(编写多线程代码)、ECS(编写高性能代码)、Burst Compliler(编译生成高性能代码)。通过使用DOTS可以让Unity项目跑的更高效,在移动平台上可以表现为更高的fps,更低的电池消耗,更小的发热。另外关于ECS可以查看本博客的另一篇简明教程。
如何使用DOTS
(一)安装环境
先确认下版本,这里推荐最新Unity版本,博主这里用的是Unity2019.3正式版;
1.打开菜单栏-window->package manager,右侧的Advanced下拉切换为show preview package(截至目前2020.2.9依旧是预览版),安装:Burst、Entities、Jobs、Hybrid Renderer(用于DOTS的渲染相关)、Unity Physics(用于DOTS的高性能物理组件)
2.启用Entity Debugger(调试器),菜单栏-Window-Analysis-Entity Debugger,可以自行拖拽窗口到合适位置方便查看调试信息;
(二) 创建一个简单实例
(1)创建子场景:新建一个场景,创建一个Cube,移除Box collider组件,选中并右键New SubScene From Selection,当然也可以自行创建一个空物体,挂载SubScene脚本。
创建后则会自动在Assets下产生一个场景文件及对应的缓存文件。可以在Assets下自行更改刚创建的SubScene的名称,本质上还是一个Unity Scene,不一样的是Cube所挂载的父节点,多挂了一个SubScene脚本而已,用于管理和标记此场景是一个DOTS的SubScene。
在SubScene脚本下,可以进行一些配置,包括对应的场景、面板hierarchy 颜色,是否自动载入场景,关闭和启用编辑子场景,保存等。打开编辑时,可以在SubScene节点下自由编辑填充其他内容,可以参考project tiny 的小赛车项目,里面就将整个大地图就被作为了一个子场景。关闭编辑时,则整个子场景的子节点不可见,也不可编辑,被当作一个GameObject。
打开子场景编辑后,查看Entity Debugger,点击右边的All Entities(Editor World)可以看到当前子场景的一些状态信息,参考(一)2.图
(2)编码
创建一个C#脚本,Rotate.cs,没什么,就是一个结构体,它将作为数据:
using Unity.Entities; public struct Rotate:IComponentData { public float radiansPerSecond; }
创建第二个脚本,RotateAuthoring.cs,主要作用是将GameObeject转化为Entity:
using UnityEngine; using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; public class RotateAuthoring : MonoBehaviour,IConvertGameObjectToEntity { [SerializeField] private float degresPerSecond; public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { dstManager.AddComponentData(entity, new Rotate { ratiansPerSecond = math.radians(degresPerSecond) }); dstManager.AddComponentData(entity, new RotationEulerXYZ()); } }
IConvertGameObjectToEntity接口用于将当前的GameObject(Cube)进行转换为DOTS的entity,然后绑定数据;
其中RotationEulerXYZ是transform的旋转,为了提高性能,DOTS里重写了一套组件,将原来的Transform里的旋转、位置、缩放等都单独出来。
第三个脚本是RotateSystem,继承自ComponentSystem,作用是做entity的行为驱动。
using Unity.Entities; using Unity.Transforms; public class RotateSystem : ComponentSystem { protected override void OnUpdate() { Entities.ForEach((ref Rotate rotate, ref RotationEulerXYZ euler) => { euler.Value.y += rotate.ratiansPerSecond * Time.DeltaTime; }); } }
Entities.ForEach用于遍历当前所有的entity,然后将数据赋值给entity进行操作;
编写代码完毕,就将第二个脚本RotateAuthoring挂载在Cube上。设置DegresPerSecond的值为50:
运行项目,cube开始旋转。查看status:
(3)使用JobSystem进行优化
修改RotateSystem.cs脚本:
using Unity.Entities; using Unity.Jobs; using Unity.Transforms; public class RotateSystem : JobComponentSystem { private struct RotateJob : IJobForEach<RotationEulerXYZ, Rotate> { public float deltaTime; public void Execute(ref RotationEulerXYZ euler, ref Rotate rotate) { euler.Value.y += rotate.ratiansPerSecond * deltaTime; } } protected override JobHandle OnUpdate(JobHandle inputDeps) { var job = new RotateJob { deltaTime = Time.DeltaTime }; return job.Schedule(this, inputDeps); } }
主要变动是将继承改为了JobComponentSystem,增加了一个RotateJob,然后到OnUpdate中调用job;
(4)使用BurstCompile
在菜单栏中启用BurstCompile
在RotateSystem代码中的RotateJob结构体上添加[BurstCompile]特性,需要using Unity.Burst;
[BurstCompile] private struct RotateJob : IJobForEach<RotationEulerXYZ, Rotate> { public float deltaTime; public void Execute(ref RotationEulerXYZ euler, ref Rotate rotate) { euler.Value.y += rotate.ratiansPerSecond * deltaTime; } }
(5)验证
开启SubScene的编辑,并大量复制cube,3000个以上。保存子场景。运行,开启unity编辑器的Status来查看:
我们发现,帧率依旧很稳定,只是Batches飙高到了3359。我们也需要优化掉:
(6)优化Batches
在Project面板下创建一个Material,命名为cube,并勾选Enable GPU Instancing.
选中子场景的所有cube,并将材质赋值到所有cube上。保存子场景,再次运行,发现batches已经获得了优化,帧率也大幅度提高。
小结
DOTS带来的性能改进是十分客观的,可以想象一些弹幕游戏、塔防游戏,使用上DOTS是可以非常直观的看到性能改进的。目前Unity也在大力推这套开发技术,相信未来会有越来越多的项目使用DOTS,并得益于DOTS.
Over~
转载请注明出处。
2020.2.9 优三帝同学 By Mark
博主公众号:优三帝
干货十足!讲解到位。
请求加上dots模式发布H5版本的操作流程,哈哈。
Entity.IJobForEach方法已经在2020.6.20过时了。。。
对,DOTS一直在迭代,所以目前还在beta中,尝鲜可以,正式使用预计还要些时间吧…
第三个脚本RotateSystem在VS里报错了,博主知道怎么修改吗,大概=>那里
=> 这个改成 ” => ” lambda表达式,我这个页面编辑器自动把箭头转义为>了。。尴尬