草庐IT

【Unity】Entities 1.0 学习(一):Aspect

魔术师Dix 2023-09-04 原文

        Unity在 2022年下半年(我印象是9月份左右)推出了 Entities 1.0 ,可以在 2022.2.0b8 以上的版本使用。当时我粗略地看了一下,但是没有深入学习。最近空闲时间稍多,就认真来学习一下 Entities 1.0有啥新的东西。

        1.0 毕竟是大更新,改变的东西还是很多,所以应该会断断续续分好几次更新。

        首先来看官方的更新文档:

What's new in Entities 1.0 | Entities | 1.0.0-exp.12https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/whats-new.html        新增的功能有很多了,我就不一一列举了,需要的同学自行看文档了。我就一个一个的来看,学到哪里算哪里。

        这篇文章是 Entities 1.0 的介绍,不是 ECS 相关的入门。我默认你已经熟练使用之前版本的 Unity ECS,只是对 1.0 的更新缺乏了解的。如果没有 ECS 的相关知识,不建议阅读此文,建议先学习 ECS 并有初步的理解。

        我目前看的版本是 1.0.0-exp.12 ,后续Unity可能会做一些更改,但是应该不会很大。

1、什么是Aspect

        简单地理解就是 ComponentData 的二次封装 。

        Aspect 是 1.0 新增一个新特性,允许将多个CompoenentData 或是 Buffer 组合在一起,这样使用起来就更加方便,感觉更像面向对象了。

Organize your code with aspects | Entities | 1.0.0-exp.12https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/aspects-intro.html        例如我在某个Entity上挂载了多个 ComponentData 时,我可以通过 Aspect 将他们全部拿到并自定义一些方法,而不必要再像以前那样拿到各个单独的 ComponentData 然后分别修改。不但在设计上更为友好,同时还能大量减少重复代码。


关于重复代码:

        在以前的书写中,如果某段逻辑在多个 System 中都会使用,要么是重复写代码,要么放到一个公共类。而放到公共类中,如果涉及到大量参数传递时也会很恶心。

        而在 Aspect 中,我们可以将公共方法直接卸载 Aspect 中,其值获取可以直接由内部属性构造获取,非常方便。        


2、构建 Aspect

        接下来从一个实例开始介绍 Aspect :

public readonly partial struct BuildAspect : IAspect
{
    //下面的 UTransform 和 UBounds 都是自定义的 ComponentData 
    private readonly RefRW<UTransform> mTransfrom;
    private readonly RefRO<UBounds> mBounds;

    //RenderPartParentBuffer_Child 是自定义的的 BufferElementData
    private readonly DynamicBuffer<RenderPartParentBuffer_Child> mChilds;
}

        readonly partial 是固定写法,这个不能改,然后继承 IAspect,并且添加至少一个 RefRO / RefRW 即可完成对 Aspect 的构建。

        当然,在 Aspect 里也是可以嵌套其他 Aspect 的。

        在代码编译阶段,Unity会根据你写的 Aspect 代码自动生成(派生类)对应的创建代码,以及Handler(下文会看到,在System内部使用的)之类,这种机械代码完全就可以不用写了。


关于:IAspectCreate<T>

        如果你浏览 Unity.Transforms.TransformAspect 的时候,你会发现他继承了这个 IAspectCreate<T>。但实际上我们自己写的时候是不需要的,只需要继承 IAspect 即可。实际上当我们阅读 TransformAspect 发现非常复杂,实现绕来绕去的,是因为我们看到的代码包含了自动生成的代码。而我们自己写的时候是不需要写这些的。

        当继承 IAspect 之后,编译时 Unity 会自动生成一个派生并继承 IAspectCreate<T>,并自动写完所需要的代码。如果我们自己继承了 IAspectCreate<T> ,还需要自己写生成代码。


        当然,就上文的代码还看不出有什么用,但是我们将其功能完善之后你就能看出端倪了:

    public readonly partial struct BuildAspect : IAspect
    {

        private readonly RefRW<UTransform> mTransfrom;
        private readonly DynamicBuffer<RenderPartParentBuffer_Child> mChilds;
        private readonly RefRO<UBounds> mBounds;

        public float3 Position
        {
            get { return mTransfrom.ValueRO.Position; }
            set { mTransfrom.ValueRW.Position = value; }
        }

        public quaternion Rotation
        {
            get { return mTransfrom.ValueRO.Rotation; }
            set { mTransfrom.ValueRW.Rotation = value; }
        }

        public UBounds Bounds => mBounds.ValueRO;

        public RenderPartParentBuffer_Child GetFirstChild => mChilds[0];

    }

        显然,这里面就和我们平时写的C#就差不多了,几乎没有限制,可以自由地调用现有属性完成逻辑。我们大可以在这个基础上完成一些逻辑的封装,从而使ECS代码更接近大家熟知的面向对象模式。

3、主线程调用 Aspect

        主线程只需要通过EntityManager来获得这个Aspect即可:

Entity e = Entity.Null;//示例;
EntityManager mananger = default;//示例;

var buildAspect = mananger.GetAspect<BuildAspect>(e);
buildAspect.Position = 0;//设置位置 Test

        很显然,看得出来Aspect所谓的获取,其实就是重新构建一个新的结构体,Unity并没有设计一个缓存之类的。

        在主线程调用这个非常友好,直接都可以当做一个Class来使用都是没有问题的。

4、System中调用Aspect

        相比较而言,在System中调用就麻烦很多了。


        非常搞笑的是,Unity的官方文档里说在System里可以用SystemAPI来创建Aspect:

         实际上这个API根本不能用,里面就没有实现,谁调用谁报错(笑)。

        大家可以看看官方文档,是不是我的理解有问题。


4.1、在 IJobEntity 中使用 Aspect

        在 IJobEntity 的写法则非常简单,啥预处理都不用,直接像下面这样写就可以:

using Unity.Entities;
using Unity.Burst;

// It's best practice to Burst-compile your code
[BurstCompile]
partial struct CannonBallJob : IJobEntity
{
    void Execute(ref CannonBallAspect cannonBall){
        // Your game logic
    }
}

        就这样就轻松愉快地可以用起来了。

4.2、在 IJobChunk 中使用 Aspect

        而在 IJobChunk 中用起来麻烦多了,需要做的工作不是多一点点:

public partial class EcsTestBuildSystem : SystemBase
{
    private EntityQuery mEntityQuery;
    private BuildAspect.TypeHandle _aspectTypeHandle;//声明一个Handle

        protected override void OnCreate()
        {
            base.OnCreate();
            mEntityQuery = GetEntityQuery
                (
                    ComponentType.ReadWrite<UTransform>(),
                    ComponentType.ReadWrite<RenderPartParentBuffer_Child>(),
                    ComponentType.ReadOnly<UBounds>(),                   
                  );

            _aspectTypeHandle = new BuildAspect.TypeHandle(ref CheckedStateRef, false);
        }
        
        protected override void OnUpdate()
        {           
            _aspectTypeHandle.Update(ref CheckedStateRef);//更新

            Dependency = new EcsTestRunner_Update()
            {
                BuildAspectHandle = _aspectTypeHandle//传值
            }.ScheduleParallel(mEntityQuery, Dependency);
        }
        
        [BurstCompile]
        private struct EcsTestRunner_Update : IJobChunk
        {
            public BuildAspect.TypeHandle BuildAspectHandle;

            public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
            {
                var builds = BuildAspectHandle.Resolve(chunk);

                for (int i = 0; i < chunk.Count; i++)
                {
                    var build = builds[i];
                }
            }
        }

}

        瞬间代码量就上去了!


        在  IJobChunk 中使用Aspect之所以麻烦,是因为这里几乎所有的代码都是要我们自己写的。而在 IJobEntity 中,大部分代码都是 Unity 在派生类中帮助我们写好了。

        但是我还是建议使用 IJobChunk , 麻烦但是有意义的。你可以理解为这个是 Job 的一种高级写法,我们可以通过这个控制 Chunk,虽然麻烦了点,但是可以获取更高的效率。如果要真的用好 ECS ,IJobChunk 肯定是家常便饭。


5、其他注意事项

  • 构建 Aspect 是会有额外消耗的,毕竟每次都会创建一个新的。所以如果追求极致性能就不要使用这个。

  • 在 Aspect 的写入其实是单线程的,如果是多线程写入会提示线程竞争。所以要么单线程跑,要么加上 NativeDisableParallelForRestriction 属性。

  • Aspect 在以读写的方式构造时,无论改了哪些属性,都会将里面的所有值设置为修改,自然也会损失一些性能。构建一个 Aspect 很容易,但是要构建一个高效率的 Aspect ,还是非常困难的。

有关【Unity】Entities 1.0 学习(一):Aspect的更多相关文章

  1. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  2. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  3. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  4. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  5. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  6. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  7. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  8. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  9. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  10. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

随机推荐