Unity的Dots技术入门

前言

看过Dots技术宣传片,当时确实被震惊到了,脑袋里闪过一句话:时代变了!大人。DOTS是Data-Oriented Technology Stack(数据导向的技术栈):借助Unity全新高性能多线程式数据导向型技术堆栈(DOTS),充分利用当今的多核处理器。游戏的运行速度会变得更快。

1.Unity的Dots由来

这种技术在游戏领域最先成功的案例来自守望先锋,各位可以去看看《守望先锋》架构设计与网络同步,ECS全称是Entity Component System,即实体、组件和系统。

  • 实体一般用途的对象,可以用唯一ID来标识。
  • 组件是一组数据,可以是实体一部分,也可以是实体和世界交互的中间数据(注意:没有功能)。实体由若干组件组成的,否则它是空盒子。
  • 系统基于实体中的特定组件来实现特定功能,但是系统是集中批量处理所有的实体的。

ECS这种架构有什么好处呢?相比OOP方式实现Component,ECS中相同组件的数据是连续的,System集中处理这些组件,可以方便利用CPU Cache提升效率。由于System处理的可能是几个组件,ECS将组件尽量的最小化(否则会有很多数据冗余),这样就实现数据的扁平化。同时System间禁止相互调用,最大程度的实现代码解耦。而OOP最被诟病的调用层次深,结构复杂等问题都被解决了。
Dots就是由实体组件系统(ECS)、任务系统(Job System)、Burst Compiler编译器三部分组成。DOTS保证相同类型组件在内存中都是顺序排列,极大程度增加缓存的命中率,此外配合任务系统(Job System)让开发者无需头疼多线程同时访问数据需要手动加解锁的麻烦,最终加持Burst Compiler让性能飞起来。

2.与OOP编程对比

在对比之前,需要了解CPU相关概念:

  • 1.CPU处理数据速度非常快,内存条和硬盘跟不上它的速度。
  • 2.CPU自身有三级缓存,用来提高CPU获取数据速度。n级缓存最快(n是1->3),但容量最小。
  • 3.内存是指CPU拿取数据的最开始的地方,CPU访问内存速率远小于三级缓存速率。
  • 4.CPU操作数据会从三级缓存中取得数据,速度非常快,但有些情况下数据不在三级缓存中(缺页中断),就需要寻址到内存中,并且把目标数据重新放到三级缓存中,提高下一次访问速度。

很多传统游戏引擎是基于面向对象来设计的,游戏世界中的东西都是对象(GameObject),每个对象都有叫做Update方法,游戏引擎遍历所有对象,依次调用其Update方法。有些引擎甚至定义多种Update方法,在同一帧的不同时机去调用(Unity通过反射机制调用这些Update函数)。 OOP编程模式其实是有极大的缺陷的。因为游戏对象由很多部分聚合而成,引擎的功能模块很多,不同模块关注的部分往往互不相关。比如音效模块数据并不需要关心物理碰撞数据、渲染模块数据不关心游戏逻辑数据,对于某种模块没有必要的数据保存到CPU中,我们可以称为垃圾数据。从自然意义上说,把游戏对象属性聚合是很正常的事情,对象生命期管理也是最合理的方式。但这种对象大量存在时就会严重性能问题,接下来展示Unity的MonoBehaviour所继承的Component子类。

exp1

其实MonoBehaviour继承关系是这样的,Behaviour->Component->Object,这些子类全部加起来的数据、属性可不是以上这么点,有兴趣可以进去看看这些子类都定义了什么数据、属性、方法。MonoBehaviour对象过多会导致大量无用数据占据CPU的缓存(比如控制transform旋转,里面却有音效、物理、相机等等属性),结论是传统引擎的设计思路对CPU缓存不太友好导致利用率不高,出现CPU缺页中断情况会比较频繁,下面给出图解:

exp2

使用Ecs技术对CPU缓存利用率有质的提高,因为保存到CPU缓存的数据都比较纯净(ECS中的实体、组件),这样可以有效降低CPU缺页中断,所以Dots相对于OOP编程有以下优点:

  • 1.ECS对CPU缓存友好,减少CPU缺页中断,防止CPU获取数据慢,从而CPU出现等待情况。
  • 2.JobSystem编写多线程代码可以提供高性能,显著提高帧速率和延长移动设备的电池寿命。
  • 3.Burst Compiler,用于生成高度优化的本地代码。

3.Dots官方案例

拉取Unity官方案例AngryBots_ECS,博主使用Unity2020.2.0a打开案例工程,发现一大堆的问题和报错(剩下渲染管线方面问题无法解决,但不影响运行),接下来分析如何一步步解决这些问题。error首先是Hybrid Renderer版本问题,看报错好像依赖了High Definition RP包(但是下载RP后还是不行),所以这里尝试把Hybrid Renderer更新到最新的版本,于是第一个报错就消失了。出现了以下报错就和接龙一样…error1Job System不存在OnCreateManager接口,可以进入到基类ComponentSystemBase里,发现了OnCreate生命周期函数,可以重写它来代替报错函数。error2World.Active去代替World.DefaultGameObjectInjectionWorld,可能Active是老版本的命名方式,GameObjectConversionUtility.ConvertGameObjectHierarchy接口所需参数也不一致,可以改成一下代码段:

	var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
	manager = World.DefaultGameObjectInjectionWorld.EntityManager;
	bulletEntityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(bulletPrefab, settings);

error3最后TimeData.deltaTime改成大写开头即可,全部报错改好后会发现运行时还有问题,可能是Burst Compiler的问题,重启Unity后所有问题都得到解决了。
如果你终于走到这步,可能会很高兴。但启动游戏后会发现看不到子弹和敌人…不使用Ecs是可以看到子弹的,如下图所示:

exp3

ps:莫名其妙死亡的话,只是看不到的敌人将主角杀死,上面说过可能是渲染管线的问题,经过在下长时间努力,大概知道问题所在了。尝试Edit/Project Settings/Graphics下将Scriptable Render Pipeline Settings设置成none,可以看到一下画面(可以看到子弹、敌人)。

exp4

具体解决方案不太清楚,如果有人知道麻烦下面留言,万分感谢~
感觉GameObjectConversionSettings或Hybrid Rendering问题,但不知道如何修改。
案例演示视频

4.实战DOTS技术

使用Unity2020.2.0a创建新工程后,需要导入hybrid、entities(entities依赖jobs、burst),如下图所示:exp5低版本Unity可能不存在此文件,并且manifest.json也不存在jobs、burst这些依赖包,这时需要手动拉取。接下来讲述下3种拉取方式:

  • 使用Package Manager拉取需要包(可能搜索不出来)。
  • 打开Package Manager后,窗口左上角有个加号,点开有三个选项Add package from xxx(有时低版本不出现Package Manager)。
  • 在manifest.json里添加两个依赖包(最稳定的方法):
    “com.unity.entities”: “0.14.0-preview.18”,
    “com.unity.rendering.hybrid”: “0.8.0-preview.18”,

依赖包全部导入后,接下来先尝试用DOTS技术写多个Cube旋转吧。

  • 1.创建旋转方块组件:
using Unity.Entities;

public struct RotationCubeComponent : IComponentData
{
    public float speed;
}

注意必须是结构体,以后文章会分析为什么是结构体,如何才可以使用类,结构体比类的优点等等。

  • 2.创建旋转方块事件
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Jobs;

public class RotationCubeSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = Time.DeltaTime;
        Entities
           .WithName("RotationCubeSystem")
           .ForEach((ref RotationCubeComponent rotationSpeed, ref Rotation rotation) =>
           {
               rotation.Value = math.mul(
                   math.normalize(rotation.Value),
                   quaternion.AxisAngle(math.up(), rotationSpeed.speed * deltaTime));
           })
           .ScheduleParallel();
    }
}

ScheduleParallel已经使用Job System多线程技术了,不使用多线程处理可以自行百度。

  • 3.创建实体管理器
using Unity.Entities;
using UnityEngine;

public class EntitiesManger : MonoBehaviour, IConvertGameObjectToEntity
{
    public float FloCubeSpeed = 10f;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var data = new RotationCubeComponent { speed = FloCubeSpeed };
        dstManager.AddComponentData(entity,data);
    }
}

GameObject转Entity,继承IConvertGameObjectToEntity重写Convert函数。

  • 4.批量生成实体
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

public class EntitiesBuilder : MonoBehaviour
{
    public GameObject cube;

    void Start()
    {
        var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
        var prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(cube, settings);
        var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;

        for (var x = 0; x < 100; x++)
        {
            for (var y = 0; y < 100; y++)
            {
                var instance = entityManager.Instantiate(prefab);
                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 });
            }
        }
    }
}

将EntitiesManger和Convert To Entity挂载到Cube之后,制作成Prefab即可。然后将EntitiesBuilder随便挂载到场景的物体。
具体原理分析和DOTS深度的东西后面文章会说明,至于官方案例子弹为什么不显示,博主知道具体原因也会更新的。

5.总结

既然DOTS这么牛逼,为啥还没普及?很简单,DOTS现阶段在完成度上就是接近废品的残次品,虽然经过Unity长达近三年的宣传,DOTS到今天完成度依然极低,基本上处于不可用的状态,一款游戏引擎必不可少的部分可简单列为下面八个,要把Dots技术推动起来,游戏引擎要进行大量重构,或者保留传统的MonoBehaviour将Dots技术插接进去,接下来看看Unity对这八大模块完成情况。

  • Physics-3分 物理自然是游戏引擎不可或缺的部分,DOTS配套的Unity Physics处于非常早期的状态,Authoring工具几乎为零,甚至Joint或者CharacterConroller都要从Sample里找代码,由于它提供了一套新的API,开发过程只能用极难使用来形容,来看看Trigger事件代码就知道了,而使用过程中也是Bug层出不穷,经常会遇到类似Mesh Collider Bake错误,而与之同期宣传的Havok Physics for Unity更是奇妙,在长达半年的时间里,只是单纯引入Havok便会导致HDRP的渲染出问题.而更进阶的布料、粒子、地形等也处于几乎为零的状态。
  • Graphics-3分 DOTS与之配套的是图形系统是Hybrid Renderer,不过目前也只支持Mesh Renderer,什么Particle、Trail、VFX都还没影儿,更可惜的是官方现在优先关注于HDRP兼容性(虽然也不怎么样),URP或者Built-In Pipeline就基本别想了,遇到了问题也只能双手一摊。
  • Audio-0分 Unity目前只开发了底层的 DSP 系统,上层的DOTS Audio说了一两年了连影子都没有,完成度接近于零。
  • Animation-0分 Unity Animation依然处于极早期的开发中,DOTS Sample展示了些最基本的使用,类似 Animator 这样的高层方案现在还没影儿。
  • Network-2分 也是Unity吹了两年的东西,DOTS Sample中用的NetCode只不过把FPSSample中的部分代码挪过去了,目前版本号 0.0.4,意思就是太早期了别用。
  • UI-0分 IMGUI,UI Element和UGUI三个Unity的产品均不支持DOTS。
  • AI-0分 Navmesh,Ml-Agent,Ai Planner三个Unity的产品均不支持DOTS。
  • Input-2分 老的Input System相对简单与DOTS没啥关系,可新的Input System居然也不支持DOTS,幸运的是InputSystem相对独立,嫁接进DOTS并不困难。

对于游戏引擎模块全部改成DOTS是有难度的,不然Unity公司对于DOTS支持程度就不是如此了(毕竟对游戏行业算是革命),在下大胆预言未来有某个天才或Unity公司完全实现了DOTS并且达到可以商用的地方,将会是巨大的利润和商机。大家可能比较关心的是目前DOTS能不能接入游戏,只能说部分模块可以使用(某些经典批处理场景),个人建议不要把还没成型的技术接入到项目中,现在DOTS还有很多限制,知道有这个东西即可。参考资料和案例:DOTS开源案例DOTS参考资料DOTS实用案例

  • 8
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Unity DOTS(DOTS: Data-Oriented Technology Stack)是Unity的一种数据导向技术堆栈,它旨在提高游戏性能和可扩展性。它基于实体组件系统(Entity-Component-System,ECS)的概念,其中游戏对象被拆分为实体(Entity)和组件(Component),并通过系统(System)进行处理。 在Unity DOTS中,可以使用实体组件系统来管理和处理游戏对象。通过将游戏对象转换为实体和组件的形式,可以实现高效的数据处理和并行计算。例如,在创建实体时,可以使用GameObjectConversionUtility.ConvertGameObjectHierarchy函数将GameObject转换为Entity,并使用IConvertGameObjectToEntity接口进行自定义转换。然后,可以使用系统(System)对实体和组件进行处理,例如旋转方块事件。 Unity DOTS的优势包括更高的性能,更好的可扩展性和更方便的并行计算。通过采用数据导向的设计,可以减少内存访问和数据处理的开销,从而提高游戏的帧率和响应性。 总而言之,Unity DOTS是一种数据导向的技术堆栈,通过实体组件系统和并行计算来提高游戏性能和可扩展性。它可以通过转换游戏对象为实体和组件的形式,并使用系统进行处理来实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [UnityDots技术入门](https://blog.csdn.net/m0_37920739/article/details/108181541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Unity DOTS简明教程](https://blog.csdn.net/mango9126/article/details/105219215)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值