美文网首页Unity探路营
在ECS系统中使用Job.WithCode

在ECS系统中使用Job.WithCode

作者: 洪智 | 来源:发表于2020-12-03 11:42 被阅读0次

    洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智学习DOTS。

    Entities.ForEach是最常见也是最简单的遍历entity的方式,除此之外还有一些更灵活的方式在System中执行Job。这节咱们来看一下万金油Job.WithCode

    使用Job.WithCode

    SystemBase类提供的Job.WithCode可以通过lambda函数构造单个后台运行的job,这种方式非常简单。Job.WithCode还可以在主线程上运行,并且还可以利用Burst编译来加快执行速度。

    下面这个例子使用一个Job.WithCode lambda函数,用随机数填充一个native数组,并使用另一个job将这些数字加在一起:

    public class RandomSumJob : SystemBase
    {
        private uint seed = 1;
    
        protected override void OnUpdate()
        {
            Random randomGen = new Random(seed++);
            NativeArray<float> randomNumbers
                = new NativeArray<float>(500, Allocator.TempJob);
    
            Job.WithCode(() =>
            {
                for (int i = 0; i < randomNumbers.Length; i++)
                {
                    randomNumbers[i] = randomGen.NextFloat();
                }
            }).Schedule();
    
            // 要想获取job中的数据,必须使用NativeArray,即使只有一个值
            NativeArray<float> result
                = new NativeArray<float>(1, Allocator.TempJob);
    
            Job.WithCode(() =>
            {
                for (int i = 0; i < randomNumbers.Length; i++)
                {
                    result[0] += randomNumbers[i];
                }
            }).Schedule();
    
            // 下面这句代码会立即完成已调度的job
            // 但是性能更好的做法是在一帧的早些时候调度job
            // 在一帧的晚些时候获取job的执行结果
            this.CompleteDependency();
            UnityEngine.Debug.Log("The sum of "
                + randomNumbers.Length + " numbers is " + result[0]);
    
            randomNumbers.Dispose();
            result.Dispose();
        }
    }
    

    注意:要运行并行作业,需要实现IJobFor,然后可以在系统OnUpdate()中使用ScheduleParallel()方法进行调度。

    捕获局部变量

    你不能将参数传递给Job.WithCode的lambda函数或返回一个值。不过,你可以在OnUpdate()函数中捕获局部变量。

    当你使用Schedule()调度job时,还有以下额外的限制:

    • 捕获的变量必须声明为NativeArray或者其他Native容器类型或blittable类型。
    • 要返回数据,即使数据是单个值,也必须将返回值写入用于捕获数据的nativearray。(注意,使用Run()时,lambda是在主线程中执行,你可以写入任何捕获的变量。)

    Job.WithCode提供了一组方法,将只读和安全的属性捕获到native容器中。例如,你可以用WithReadOnly表示不会更新容器,使用WithDisposeOnCompletion在作业完成后自动释放容器。(Entities.ForEach提供了相同的功能。)

    执行lambda函数

    有两种方法来执行lambda函数:

    • Schedule()-将函数作为单个非并行作业执行。作业在后台线程上运行代码,因此可以更好地利用可用的CPU资源。
    • Run()-立即在主线程上执行该函数。在大多数情况下,可以使用Burst.WithCode进行Burst编译,因此即使Job.WithCode仍在主线程上运行,代码执行也可以更快。

    请注意,调用Run()会自动完成Job.WithCode构造的所有依赖关系。如果未将JobHandle对象显式传递给Run(),系统则假定当前Dependency属性表示函数的依赖关系。(如果函数没有依赖关系,可以传入new JobHandle)

    依赖关系

    默认情况下,系统使用Dependency属性管理与ECS相关的依赖关系。默认情况下,系统将按Entities.ForEach和Job.WithCode创建的每个作业在OnUpdate()函数中出现的顺序按它们添加到Dependency作业句柄中。你还可以通过将JobHandle传递给函数来手动管理作业依赖关系,然后返回结果依赖关系。

    扩展阅读

    【扩展学习】洪流学堂回复DOTS可以阅读本系列所有文章,更有视频教程等着你!


    呼~ 今天小新絮絮叨叨的真是够够的了。没讲清楚的地方欢迎评论,咱们一起探索。

    我是大智,你的技术探路者,下次见!

    别走!点赞收藏哦!

    好,你可以走了。

    相关文章

      网友评论

        本文标题:在ECS系统中使用Job.WithCode

        本文链接:https://www.haomeiwen.com/subject/xqtjiktx.html