Unity DOTS简明教程

什么是DOTS

首先,先来了解下什么是DOTS?

DOTS是Data-Oriented-Tech-Stack,官方中文翻译是:多线程式数据导向型技术堆栈。

它主要由三部分组成:

  1. 任务系统
  2. 实体组件系统
  3. 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.

猜你喜欢

转载自blog.csdn.net/mango9126/article/details/105219215