びぼうろくってみんなやってる

みんなやってるからぼくもやる

JobSystemおべんきょう

Unity JobSystem

  • テラシュールブログ様の写経

  • JobSystemは並列処理らしい。

    • 並列処理が簡単にできるぞ的な。
    • あと書き方間違ってたらエラー吐いてくれる(優しい)。
    • 値型のみ使える。
    • 参照型は使えない。
  • NativeArrayなるもの。

    • NativeArrayは、ネイティブメモリのバッファをマネージコードに公開し、マネージドとネイティブの間でデータを共有することを可能にします。(公式 with Google翻訳)
    • 速いらしい。
    • 要素をあとから増やせない。
    • 構造体structのみ使える。
    • クラスclassは使えない。
    • Allocator(メモリの割当タイプ)を自分で決める必要がある。
      • Temp 割当と開放が速いらしい。1F内で開放する必要がある。
      • Persistent 割当と開放が遅いらしい。永続的な割当。Destroy,Disable等で開放する。
    • 使い終わったら必ず開放する必要がある。.Dispose();

    • TransformAccessArrayなるものもある。

      • 通常のComponentと違い、参照型なのにJobSystemで扱える。
    • 以下宣言から開放のサンプル。

using System.Linq;
using UnityEngine;
using Unity.Collections;

public class JobSystemTest : MonoBehaviour
{
  TransformAccessArray targets;
  NativeArray<RaycastCommand> commands;
  int length = 10;

  void OnEnable()
  {
    var targetList = new List<Transform>();
    targets = new TransformAccessArray(targetList.ToArray())
    commands = new NativeArray<RaycastCommand>(length, Allocator.Persistent);
  }

  void Update()
  {
    var results = new NativeArray<RaycastHit>(length, Allocator.Temp);

    //---
    //いろいろやる
    //----

    results.Dispose();
  }

  void OnDisable()
  {
    commands.Dispose();
    targets.Dispose();
  }
}
  • IJobParallelForなるもの

    • これを継承した構造体のExecute()内に記述した処理が、各ジョブで実行される。
    • IJobParallelForTransformなるものもある
  • JobHandleなるもの

    • JobHandle。(公式 with Google翻訳)
    • IJobParallelForを継承した構造体.Schedule()の返り値がこれ。
    • .Complete()でジョブの完了を待つ
    • .Schedule()の第三引数にJobHandleを登録すると、登録したJobHandleの完了を待ってから実行。
    • RaycastCommand.ScheduleBatch()なるものもある。
void RandomBounce()
{
  for(int i = 0; i < targetCount; i++)
  {
    bounce[i] = Random.Range(0.5f, 2.5f);
  }
}

void CreateJobs()
{
  handle.Complete();
  for(int i = 0; i < targetCount; i++)
  {
    commands[i] = new RaycastCommand(targets[i].position, Vector3.down);
  }

  UpdatePosition updatePositionJob = new UpdatePosition
  {
    raycastResults = results,
    velocitys = velocity,
    bounces = bounce
  };

  ApplyPosition applyPosition = new ApplyPosition
  {
    velocitys = velocity
  };

  var raycastJobHandle = RaycastCommand.ScheduleBatch(commands, results, Mathf.FloorToInt(targetCount / 3));
  var updatePositionHandle = updatePositionJob.Schedule(targetCount, Mathf.FloorToInt(targetCount / 3), raycastJobHandle);
  handle = applyPosition.Schedule(targets, updatePositionHandle);
}

struct UpdatePosition : IJobParallelFor
{
  [ReadOnly] public NativeArray<RaycastHit> raycastResults;

  public NativeArray<float> velocitys;
  public NativeArray<float> bounces;

  void IJobParallelFor.Execute(int index)
  {
    if(velocitys[index] < 0 && raycastResults[index].distance < 0.5f)
    {
      velocitys[index] = bounces[index];//Random.Rangeとかは使えない
    }
    velocitys[index] -= gravity;
  }
}

struct ApplyPosition : IJobParallelForTransform
{
  public NativeArray<float> velocitys;

  void IJobParallelForTransform.Execute(int index, TransformAccess transform)
  {
    transform.localPosition += Vector3.up * velocitys[index];
  }
}
  • [ReadOnly]が以外と大事だったりするのでまた書かないとならない。

  • 成果物。

using System.Linq;

using UnityEngine;
using UnityEngine.Jobs;

using Unity.Collections;
using Unity.Jobs;

public class JobSystemTest : MonoBehaviour
{
    [SerializeField] CanvasGroup canvasGroup;
    Transform[] transforms;

    NativeArray<RaycastCommand> commands;
    NativeArray<RaycastHit> results;

    NativeArray<float> velocity;
    NativeArray<float> bounce;

    TransformAccessArray targets;

    const int RaycastDistance = 130;
    const float gravity = 0.098f;

    int targetCount;

    JobHandle handle;

    void OnEnable()
    {
        var targetList = GetComponentsInChildren<Transform>().ToList();
        targetList.Remove(transform);
        targetCount = targetList.Count;

        foreach(var target in targetList)
        {
            target.localPosition = new Vector3(Random.Range(5, 45), 4, Random.Range(5, 45));
        }

        commands = new NativeArray<RaycastCommand>(targetCount, Allocator.Persistent);
        results = new NativeArray<RaycastHit>(targetCount, Allocator.Persistent);
        velocity = new NativeArray<float>(targetCount, Allocator.Persistent);

        for(int i = 0; i < targetCount; i++)
        {
            velocity[i] = -1.0f;
        }

        bounce = new NativeArray<float>(targetCount, Allocator.Persistent);
        targets = new TransformAccessArray(targetList.ToArray());
    }

    void OnDisable()
    {
        handle.Complete();

        commands.Dispose();
        results.Dispose();
        velocity.Dispose();

        bounce.Dispose();
        targets.Dispose();

    }

    void Update()
    {
        RandomBounce();
        CreateJobs();
    }

    void RandomBounce()
    {
        for(int i = 0; i < targetCount; i++)
        {
            bounce[i] = Random.Range(0.5f, 2.5f);
        }
    }

    void CreateJobs()
    {
        handle.Complete();
        for(int i = 0; i < targetCount; i++)
        {
            commands[i] = new RaycastCommand(targets[i].position, Vector3.down);
        }

        UpdatePosition updatePositionJob = new UpdatePosition
        {
            raycastResults = results,
            velocitys = velocity,
            bounces = bounce
        };

        ApplyPosition applyPosition = new ApplyPosition
        {
            velocitys = velocity
        };

        HitCheck hitCheckJob = new HitCheck
        {
            raycastResults = results,
            result = new NativeArray<int>(1, Allocator.TempJob)
        };

        var raycastJobHandle = RaycastCommand.ScheduleBatch(commands, results, Mathf.FloorToInt(targetCount / 3));
        var isHitJobHandle = hitCheckJob.Schedule(raycastJobHandle);
        var updatePositionHandle = updatePositionJob.Schedule(targetCount, Mathf.FloorToInt(targetCount / 3), raycastJobHandle);
        handle = applyPosition.Schedule(targets, updatePositionHandle);

        isHitJobHandle.Complete();
        canvasGroup.alpha = hitCheckJob.result[0];
        hitCheckJob.result.Dispose();
    }


    struct UpdatePosition : IJobParallelFor
    {
        [ReadOnly] public NativeArray<RaycastHit> raycastResults;

        public NativeArray<float> velocitys;
        public NativeArray<float> bounces;

        void IJobParallelFor.Execute(int index)
        {
            if(velocitys[index] < 0 && raycastResults[index].distance < 0.5f)
            {
                velocitys[index] = bounces[index];//Random.Rangeとかは使えない
            }
            velocitys[index] -= gravity;
        }
    }

    struct ApplyPosition : IJobParallelForTransform
    {
        public NativeArray<float> velocitys;

        void IJobParallelForTransform.Execute(int index, TransformAccess transform)
        {
            transform.localPosition += Vector3.up * velocitys[index];
        }
    }

    struct HitCheck : IJob
    {
        [ReadOnly] public NativeArray<RaycastHit> raycastResults;
        public NativeArray<int> result;

        void IJob.Execute()
        {
            foreach(var raycastResult in raycastResults)
            {
                if(raycastResult.distance < 1.0f) { result[0] = 1; return; }
            }
            result[0] = 0;
        }
    }
}