賢人の言葉
宮沢賢治氏の代表作「雨ニモマケズ」では「雨ニモマケズ風ニモマケズ・・」の次に「雪ニモ夏ノ暑サニモマケヌ・・・」という言葉がありますが、ここ最近はさすがの賢治氏もその節を曲げそうな暑さが続いていおり、私の場合は負けっぱなしでエアコン全開の日々が続いています。もう部屋から出られんし。
夏休みシーズンに入ったこともあり私の周りでも、フェスだ!、花火だ!、お祭りだ!、みたいなワードが飛び交っていますが、私としてはこの酷暑の中、一歩も外に出る気はありません。そもそも誘われない、という話は悲しくなるので置いといて、この夏は部屋から出ることなくゲーム制作に専念したいと思っています、うん、だから泣いてません。
C# JobSystemの続き
前回の記事でUnityのJobSystemについて取り上げましたが、今回はその続きです。他にネタがなかったとか、そういうわけでは決してありません、うん、たぶん、きっとそう。
前回では複数のモデルに公転と自転の運動をさせていましたが、今回は公転運動に収縮の動きを加えてみようと思います。つまり公転半径が周期的に変わるってやつ。
公転運動に収縮運動を加える
公転運動のロジックに収縮分の計算を加えれば出来そうな気もしますが、今回は公転運動を計算した後、収縮運動を計算してオブジェクトのTransformに反映しようと思います。これにより前回作成した公転運動のロジック部分には一切手を入れなくて済みます。
収縮運動の移動
収縮運動の移動量にはAnimationCurveを使用しました。Main側でカーブの位置を取得して、変数「SpreadPower」に渡して収縮運動計算で使用しています。
Main側
// Update is called once per frame void Update () { // 公転用JOB領域設定 RevolutionMotionUpdate revolutionJob = new RevolutionMotionUpdate() { Accessor = this._revolutionStructs, DeltaTime = Time.deltaTime, }; // 自転用JOB領域設定 RotationMotionUpdate rotationJob = new RotationMotionUpdate() { Accessor = this._rotationStructs, DeltaTime = Time.deltaTime, }; //カーブ位置取得 if (_curveRate == 1f) { _curveRate = 0; } else { _curveRate = Mathf.Clamp(_curveRate + _spreadPower, 0f, 1f); } // 収縮運動用JOB領域設定 ContractionMotionUpdate contractionJob = new ContractionMotionUpdate() { Accessor = this._spreadStructs, SpreadPower = contPower * _jumpCurve.Evaluate(_curveRate) }; this._jobRevolutionHandle.Complete(); this._jobRotatioHandle.Complete(); this._jobSpreadHandle.Complete(); this._jobRotatioHandle = rotationJob.Schedule(this._planetTransformAccessArray); this._jobRevolutionHandle = revolutionJob.Schedule(this._planetTransformAccessArray); this._jobSpreadHandle = contractionJob.Schedule(this._planetTransformAccessArray, _jobRevolutionHandle); JobHandle.ScheduleBatchedJobs(); }
収縮運動側
/// <summary> /// 収縮運動のJOB /// </summary> struct ContractionMotionUpdate : IJobParallelForTransform { public NativeArray<RotCalStruct> Accessor; public float SpreadPower; // JobSystem側で実行する処理 public void Execute(int index, TransformAccess transform) { RotCalStruct accessor = this.Accessor[index]; transform.localPosition = Spread(accessor, transform); this.Accessor[index] = accessor; } // 収縮運動 Vector3 Spread(RotCalStruct data, TransformAccess transform) { Vector3 nowPos = transform.localPosition; Vector3 radiusVec = new Vector3(nowPos.x - data.RevOri_X, nowPos.y - data.RevOri_Y, nowPos.z - data.RevOri_Z); Vector3 modVec = (SpreadPower + 1.0f) * data.RevRadius * radiusVec.normalized; Vector3 retPos = new Vector3(data.RevOri_X + modVec.x, data.RevOri_Y + modVec.y, data.RevOri_Z + modVec.z); return retPos; } }
公転運動後に収縮運動を行う
並列処理を行うためのJobSystemですが、今回の場合は公転運動⇒収縮運動の順に処理を行わなくてはいけません。これを実現させるためにJobSystemでは各Jobの依存関係を設定することができます。
上のソースのMain側で
this._jobRevolutionHandle = revolutionJob.Schedule(this._planetTransformAccessArray); this._jobSpreadHandle = contractionJob.Schedule(this._planetTransformAccessArray, _jobRevolutionHandle); JobHandle.ScheduleBatchedJobs();
と記載している箇所があります。
収縮運動側のScheduleの第二引数に公転運動側のジョブハンドルを指定することで、公転運動の処理が完了したら収縮運動の処理が即時に実行させるようになります。
公転運動にも収縮運動にも同じtransformArrayを渡しているので、収縮運動の処理には公転運動分の移動が終ったtransformに収縮運動分の移動を反映させることができます。
実際の動作
これらの処理を使って実際に動作させた結果が以下の動画です。
どーん
スクエア上に並んだ球体が自転+公転しながら収縮運動もしていることが分かります。
JobSystemは難しくない
前回も述べましたがJobSystemの実装自体は難しくありません。ただ、JobSystemでどのように実装するか、という設計の見極めは十分な考慮が必要かもしれません。今回の場合だと、自転と公転はそれぞれ並列処理でよいけど、収縮は公転⇒収縮の順じゃないと駄目、みたいな。まぁ、色々試してみるのが一番ですね。