原カバンは鞄のお店ではありません。

Unityを使ったゲーム制作のあれこれを綴っていきます。

【Unity】JobSystemを使って弾を回転させながら撃つ

夏の思ひで

盆過ぎあたりから秋雨前線が北上してきた影響なのか、朝夕は涼しくなって猛暑が過ぎたことを実感できる気候になってきました。
ということは夏も終わり、と言う事なんですが、社会人になってからは夏だろうが冬だろうが代わり映えのない日々を過ごしているので、夏を振り返るとか、夏の終わりを寂しく思うとか、そんな傷心な気持ちになったりしません。

まぁ、無理やり思い返してみても、夏季休暇中に二回ライブを観に行って二回とも隣の席が巨汗デブとセーラ服を着たおっさんという奇人変人だったとか、珍しく遠出をしたら何故か上司と鉢合わせになったとか、碌な思い出しかないので大人しく季節が過ぎていくのを静観したいと思います。

 

前回の続き

そんな訳で前回の続きとなります。

前回ではJobSystemを使って弾丸を真直ぐ飛ばす処理を作りましたが、今回はそれに回転の動きを加えてみたいと思います。
まぁ、ゲームとしては弾丸が回る必要はないんですがね。

 

f:id:Karvan:20190827204755g:plain

こんな感じで回ります。

 

公転処理

今作中、自機の発射する弾丸は弾3つで1組の構成となっています。
弾丸はZ方向へ移動し、かつ弾3つの中心点を原点としてZ軸を回転軸に公転運動を行います。

f:id:Karvan:20190827204932p:plain

 

通常、このような構成で公転運動を実装する場合、公転原点に空のオブジェクトを生成し、弾丸はその子オブジェクトして定義、親オブジェクトを回転させることで弾丸を公転させる処理を取ったりします。

が、今回は各弾丸にそれぞれに対して公転運動の計算を行います。

 

公転運動の計算については、@mao_氏がQuiitaに書かれた下記の記事中の「任意点周りの回転行列の算出について」の章を参考してMatrixを使用して計算しています。(なのでソースの転記はしません。ご了承ください。)

 

qiita.com

処理順番を制御する

公転運動はMatrixで計算するとして、弾丸はZ方向へ移動し続けるので、当然、公転原点となる場所も移動します。
なのでJobSystemを使ってZ方向への移動と公転運動を行う場合は、移動計算用のJOBと公転計算用のJOBの二つを用意して、先に移動計算用JOBでZ方向へ移動させ、その完了後に公転計算用JOBで公転運動の計算と反映を行う、という順序になります。

f:id:Karvan:20190827205945p:plain
 

この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で計算するようにしました。

 

f:id:Karvan:20190827205840g:plain

結果としてはこんな感じ、3つの弾丸が回りながら一点に収束していきます。

まぁ、これもSTGとしては必要な機能ではないんですが・・・

 

◇プライバシーポリシー

●個人情報の利用目的

当ブログでは、メールでのお問い合わせ、メールマガジンへの登録などの際に、名前(ハンドルネーム)、メールアドレス等の個人情報をご登録いただく場合がございます。

これらの個人情報は質問に対する回答や必要な情報を電子メールなどをでご連絡する場合に利用させていただくものであり、個人情報をご提供いただく際の目的以外では利用いたしません。

●個人情報の第三者への開示

当サイトでは、個人情報は適切に管理し、以下に該当する場合を除いて第三者に開示することはありません。

・本人のご了解がある場合
・法令等への協力のため、開示が必要となる場合

個人情報の開示、訂正、追加、削除、利用停止
ご本人からの個人データの開示、訂正、追加、削除、利用停止のご希望の場合には、ご本人であることを確認させていただいた上、速やかに対応させていただきます。

アクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。

このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。

●免責事項

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

●プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。

修正された最新のプライバシーポリシーは常に本ページにて開示されます。