Unity DOTS技术浅析

Unity DOTS

初识DOTS

Unity的Dots技术最近是很热的,我也在闲暇之余简单学习了一下,学习新的技术会让人快乐是件不争的事实对吧(努力安慰自己……)
Data-Oriented Technology Stack,这是它的全称,翻译过来叫多线程式数据导向型技术堆栈,这个是官方的翻译,多么的高大上,它首先给我带来的感觉就是一种新的编程思想,那就是面向数据,对于习惯了面向对象编程的我们Unity从业者来讲,接受它可能挺不适应的,至少在我最近的学习感受是这样。

UnityDOTS三大杀器:ECS,JobSystem&&BurstCompiler

ECS

ecs,即Entity,Component和System ,Entity它代表的就是一个实例,一个承载关联数据和行为的实体,就好比是一张身份证ID,有了它,对于一个人来说,它的行为,信息我们可以通过检索轻松拿到,Component承载的是数据,System承载的就是行为方法。这样的架构使得数据和行为解耦,提高了代码的扩展性

JobSystem

JobSystem是Unity退出的自己的多线程管理机制,它充分解放了计算机多核多线程的运算力,能为代码的执行速度带来不小的提升,而且用户不用处理繁琐的线程管理事务,十分的方便~关于JobSystem

BurstCompiler

Unity 构建了名为 Burst 的代码生成器和编译器,它有更高的效率。

应用实例

ECS

以官方的小球旋转为例,首先要做的就是构建Component数据类型,本利很简单,数据只有旋转速度,Component代码如下:
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;

public struct RotationSpeedComponent : IComponentData
{
    public float Speed;

}

注意:ECS中的Component数据必须是实现IComponentData的结构体,为什么要是结构体不是类呢?因为struct直接在栈里分配,它不像class一样分配在托管堆里,不进行垃圾回收,实现“连续紧凑的内存布局”,相比于数据分布在内存的各个角落的class,效率也会更高~
然后是System:

using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using Unity.Transforms;


public class RotationSpeedSystem : ComponentSystem
{

    protected override void OnUpdate()
    {
        Entities.ForEach((ref RotationSpeedComponent rotationSpeedComponent, ref Unity.Transforms.Rotation translation) =>
            {

                translation.Value = math.mul(math.normalize(translation.Value),quaternion.AxisAngle(math.up(), rotationSpeedComponent.Speed*Time.deltaTime));

            });
    }
}

Unity.Mathematics是UnityECS中新写的数学类,也是提高性能的举措之一,例子中的RotationSpeedSystem 继承自 ComponentSystem,它实现OnUpdate方法,这相当于我们熟悉的MonoBehavior的Update方法,实现每帧刷新的逻辑。
本例中Entity的构建要借助于Unity.Entities中的ConvertToEntity脚本,还要实现IConvertGameObjectToEntity接口,在方法中关联Component和System,将GameObject转化为Entity,代码如下:

using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;

public class EntitiesManager : MonoBehaviour,IConvertGameObjectToEntity
{

    public float speed=3;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem conversionSystem)
    {
        var moveComponent=new RotationSpeedComponent(){Speed = speed};
        entityManager.AddComponentData(entity, moveComponent);
    }
}

这个脚本EntitiesManager 挂在GameObject身上,注意还要添加ConvertToEntity脚本~

Unity DOTS技术浅析_第1张图片
这样就简单实现了一个ECS结构,当然用官方的话,这是混合ECS的写法,它与MonoBehaviour还是有一些瓜葛~
ECS也在不断迭代更新中,最新的版本,我们可以利用[GenerateAuthoringComponent]这个属性标签对Component声明,来省去EntitiesManager 脚本中关联Component和System的操作,使得代码更简洁~

ECS结合JobSystem的使用

还是物体旋转,如果我们需要数以万计的物体旋转,是可以用JobSystem实现多线程并行处理来,官方给出的ECS结合JobSystem的使用案例:
首先Component还是一样,这次用上面提到的[GenerateAuthoringComponent]属性标签来简化代码:

using System;
using Unity.Entities;

// ReSharper disable once InconsistentNaming
[GenerateAuthoringComponent]
public struct RotationSpeed_ForEach : IComponentData
{
    public float RadiansPerSecond;
}

在System中我们用 Entities.WithName方法来找到我们要处理的Component,并用ForEach处理每个的旋转行为,代码:

using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;

// This system updates all entities in the scene with both a RotationSpeed_ForEach and Rotation component.

// ReSharper disable once InconsistentNaming
public class RotationSpeedSystem_ForEach : SystemBase
{
    // OnUpdate runs on the main thread.
    protected override void OnUpdate()
    {
        float deltaTime = Time.DeltaTime;
        
        // Schedule job to rotate around up vector
        Entities
            .WithName("RotationSpeedSystem_ForEach")
            .ForEach((ref Rotation rotation, in RotationSpeed_ForEach rotationSpeed) =>
            {
                rotation.Value = math.mul(
                    math.normalize(rotation.Value), 
                    quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime));
            })
            .ScheduleParallel();
    }
}

ScheduleParallel方法其实就是运用了JobSystem的Schedule方法来实现对多个物体旋转的并行处理。

RotationSpeed_ForEach .cs要挂在我们的预制体上,可以看到,标签自动使得脚本变成RotationSpeed_ForEach Authoring,其实框架系统自动实现了与RotationSpeedSystem_ForEach的关联
Unity DOTS技术浅析_第2张图片
然后是生成一万个预制物体的代码:

using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

// ReSharper disable once InconsistentNaming
[AddComponentMenu("DOTS Samples/SpawnFromMonoBehaviour/Spawner")]
public class Spawner_FromMonoBehaviour : MonoBehaviour
{
    public GameObject Prefab;
    public int CountX = 100;
    public int CountY = 100;

    void Start()
    {
        // Create entity prefab from the game object hierarchy once
        var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
        var prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(Prefab, settings);
        var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;

        for (var x = 0; x < CountX; x++)
        {
            for (var y = 0; y < CountY; y++)
            {
                // Efficiently instantiate a bunch of entities from the already converted entity prefab
                var instance = entityManager.Instantiate(prefab);

                // Place the instantiated entity in a grid with some noise
                var position = transform.TransformPoint(new float3(x * 1.3F, noise.cnoise(new float2(x, y) * 0.21F) * 2, y * 1.3F));
                entityManager.SetComponentData(instance, new Translation {Value = position});
            }
        }
    }
}

Entity.Instantiate()是实例外化一个Entity的API。SetComponentData讲每个实例加入EntityManager中。
我们运行下,可以看到,它的FPS还是挺高的,性能很棒~
Unity DOTS技术浅析_第3张图片
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200302103525263.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE5NzE0NDA3,size_16,color_FFFFFF,t_70

下面是用传统的GameObject.Instantiate方法复制预制体并在MonoBehaviour. Update方法中处理旋转的方法,它的性能比起ECS+Jobsystem差了不止一星半点。。
Unity DOTS技术浅析_第4张图片
以上就是对ECS 官方案例的学习解读,可能有很多不足之处,望指正~ 附官方案例源码UnityECSSamples

你可能感兴趣的:(Unity DOTS技术浅析)