夏の思ひで
盆過ぎあたりから秋雨前線が北上してきた影響なのか、朝夕は涼しくなって猛暑が過ぎたことを実感できる気候になってきました。
ということは夏も終わり、と言う事なんですが、社会人になってからは夏だろうが冬だろうが代わり映えのない日々を過ごしているので、夏を振り返るとか、夏の終わりを寂しく思うとか、そんな傷心な気持ちになったりしません。
まぁ、無理やり思い返してみても、夏季休暇中に二回ライブを観に行って二回とも隣の席が巨汗デブとセーラ服を着たおっさんという奇人変人だったとか、珍しく遠出をしたら何故か上司と鉢合わせになったとか、碌な思い出しかないので大人しく季節が過ぎていくのを静観したいと思います。
前回の続き
そんな訳で前回の続きとなります。
前回ではJobSystemを使って弾丸を真直ぐ飛ばす処理を作りましたが、今回はそれに回転の動きを加えてみたいと思います。
まぁ、ゲームとしては弾丸が回る必要はないんですがね。
こんな感じで回ります。
公転処理
今作中、自機の発射する弾丸は弾3つで1組の構成となっています。
弾丸はZ方向へ移動し、かつ弾3つの中心点を原点としてZ軸を回転軸に公転運動を行います。
通常、このような構成で公転運動を実装する場合、公転原点に空のオブジェクトを生成し、弾丸はその子オブジェクトして定義、親オブジェクトを回転させることで弾丸を公転させる処理を取ったりします。
が、今回は各弾丸にそれぞれに対して公転運動の計算を行います。
公転運動の計算については、@mao_氏がQuiitaに書かれた下記の記事中の「任意点周りの回転行列の算出について」の章を参考してMatrixを使用して計算しています。(なのでソースの転記はしません。ご了承ください。)
処理順番を制御する
公転運動はMatrixで計算するとして、弾丸はZ方向へ移動し続けるので、当然、公転原点となる場所も移動します。
なのでJobSystemを使ってZ方向への移動と公転運動を行う場合は、移動計算用のJOBと公転計算用のJOBの二つを用意して、先に移動計算用JOBでZ方向へ移動させ、その完了後に公転計算用JOBで公転運動の計算と反映を行う、という順序になります。
このJOBを待ち合わせて実行させるのは非常に簡単で、待ち合わせる側のJOBのSchedule関数にて第二引数に待ち合わせ対象のJOBハンドルを設定する、だけで実装できます。
JobHandle _jobMoveHandle; // 移動用 JobHandle _jobRevolutionHandle; // 公転運動用 void Update() { // 移動用JOB領域設定 BlockMotionUpdate obstractMoveJob = new BlockMotionUpdate() { Accessor = this._moveStructs, DeltaTime = Time.deltaTime, }; // 公転用JOB領域設定 RevolutionMotionUpdate revolutionJob = new RevolutionMotionUpdate() { Accessor = this._revolutionStructs, DeltaTime = Time.deltaTime, }; // JobSystemの完了待ち this._jobMoveHandle.Complete(); this._jobRevolutionHandle.Complete(); // 移動後のチェック UpdatedCheck(); // JobのSchedule発行 this._jobMoveHandle = obstractMoveJob.Schedule(this._transformAccessArray); this._jobRevolutionHandle = revolutionJob.Schedule(this._transformAccessArray, _jobMoveHandle); // Jobの実行 JobHandle.ScheduleBatchedJobs(); }
上記の
this._jobRevolutionHandle =
revolutionJob.Schedule(this._transformAccessArray, _jobMoveHandle);
にて、第二引数に移動用のJOBハンドルを指定して、移動計算後に公転運動の計算が行われるようになります。
移動後のチェック処理(UpdatedCheck)で、移動用JOBから引き渡された出力パラメータ(NativeArray)の移動ベクトルを元に、公転運動用の原点位置を移動ベクトル分だけ移動させて公転運動用の入力パラメータ(NativeArray)へ引き渡します。
/// <summary> /// 移動後のチェック処理 /// </summary> public void UpdatedCheck() { for (int iCnt = 0; iCnt < _moveStructs.Length; iCnt++) { // 移動ベクトルの取得 Vector3 CurrentVec = this._moveStructs[iCnt].CurrentVec; // 公転原点の移動 Vector3 RotOrigin = this._revolutionStructs[iCnt].RotOrigin; RotOrigin = RotOrigin + CurrentVec; // 公転運動用の入力パラメータへ反映 this._revolutionStructs[iCnt].RotOrigin = RotOrigin; } }
上記の処理を行う為に、移動計算用JOBでは移動ベクトルを結果として保持するように変更します。
struct MotionUpdate : IJobParallelForTransform { public NativeArray<MoveStruct> Accessor; public float DeltaTime; /// <summary> /// JobSystem側で実行する処理 /// </summary> /// <param name="index"></param> /// <param name="transform"></param> public void Execute(int index, TransformAccess transform) { MoveStruct accessor = this.Accessor[index]; // 動作フラグがOFFの場合は終了 if (accessor.ActiveFLG == 0) { this.Accessor[index] = accessor; return; } // 現在の位置取得 Vector3 nowPos = transform.position; // 移動方向ベクトル取得 Vector3 purVec = accessor.purVec; //------------------------- // 移動方向ベクトルに沿って移動 //------------------------- // 移動距離計算 float vo = accessor.Vo; float deltaDist = vo * DeltaTime; // 移動後の位置計算 Vector3 modPos = nowPos + deltaDist * purPos.normalized; // transformへ反映する transform.position = modPos; // 累積値の更新 float Amount_Dist = accessor.Amount_Dist; Amount_Dist += deltaDist; accessor.Amount_Dist = Amount_Dist; // 今回の移動ベクトル accessor.CurrentVec = modPos - nowPos; // 結果保持 this.Accessor[index] = accessor; } }
Update関数内で原点の移動を行うので公転運動計算用JOBではMatrixに沿って公転運動の計算を行うだけになります。
/// <summary> /// 公転運動用計算 /// </summary> struct RevolutionMotionUpdate : IJobParallelForTransform { public NativeArray<RotCalStruct> Accessor; public float DeltaTime; /// <summary> /// JobSystem側で実行する処理 /// </summary> /// <param name="index"></param> /// <param name="transform"></param> public void Execute(int index, TransformAccess transform) { RotCalStruct accessor = this.Accessor[index]; // 公転無しの場合は処理終了 if (accessor.isRevFLG == 0) return; // 原点位置設置 Vector3 originPoint = accessor.RotOrigin; // 公転計算用のMatrix取得 // (ここは先程のリンクの記事を参考にしてください・・・) float rotVo = accessor.CurrentAngle; Matrix matrix = CalRoundAtMatrix(Vector3.zero, accessor.RodAxis, rotVo); // 現在の位置取得 Vector3 nowPos = transform.position; // 公転計算用のMatrix取得 Vector3 CalResPos = matrix.MultiplyPoint3x4(nowPos); // 計算結果をTransformに反映 transform.position = CalResPos; // 結果保持 this.Accessor[index] = accessor; } }
これを応用して
こんな感じでJOBを待ち合わせることで色々な動作が実現することができるので、この公転運動に加えて公転半径の収縮の運動も別JOBで計算するようにしました。
結果としてはこんな感じ、3つの弾丸が回りながら一点に収束していきます。
まぁ、これもSTGとしては必要な機能ではないんですが・・・