在ECS系统中使用Job.WithCode

洪流学堂,让你快人几步。你好,我是跟着大智学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 randomNumbers
            = new NativeArray(500, Allocator.TempJob);

        Job.WithCode(() =>
        {
            for (int i = 0; i < randomNumbers.Length; i++)
            {
                randomNumbers[i] = randomGen.NextFloat();
            }
        }).Schedule();

        // 要想获取job中的数据,必须使用NativeArray,即使只有一个值
        NativeArray result
            = new NativeArray(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传递给函数来手动管理作业依赖关系,然后返回结果依赖关系。

扩展阅读

  • ECS的核心概念
  • ECS中的Entity实体
  • ECS之Component组件
  • ECS之System系统
  • 在ECS系统中使用Entities.ForEach

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


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

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

别走!点赞收藏哦!

好,你可以走了。

你可能感兴趣的:(在ECS系统中使用Job.WithCode)