i-school - 3Dレールガンシューティング 発展26
 新しい攻撃の手段として、複数の対象をロックオンしておいて、まとめて弾を発射する機能を実装します。
現在の弾数を超える対象はロックオンできないようにしたり、ロックオンした順番に弾を発射するといった制御が必要になります。


<実装動画>
動画ファイルへのリンク



発展26 ーロックオン機能の追加ー


新しい学習内容は、以下の通りです。

 ・学習済の内容の応用
 ・リファクタリング



1.設計


 現在の攻撃処理では、キーボードの攻撃用のボタン(マウスの左クリック)を押し続けることにより、
装填されている弾がなくなるまで連続射撃を行うことが出来ます。また、制御方法を切り替えることで、1発ずつの制御も出来ます。

 この他に、新しい攻撃の方法として、別の攻撃用のボタン(マウスの右クリック)を用意し、
そちらを押し続けることでマウスカーソルの位置にある対象をロックオンし、攻撃対象として予約しておきます
このロックオン機能は、現在の弾数を超える対象はロックオンの対象にはできないようにします。
つまり、ロックオンの最大数 = 現在の弾数となる制御を行います。

 その後、攻撃用のボタンを離した(マウスの右クリックをやめた)際に、ロックオンの対象数が1つ以上ある場合には、
ロックオンをした対象に対して順番に連続攻撃を行うようにします。

 例えば、A、B、C という攻撃対象がいる場合に、C、A、Bの順番でロックオンを行って攻撃の予約をしていた場合には
C、A、B の順番に1回ずつ弾を発射する攻撃を制御します。

 このとき、すでにロックオンの対象になっているものには再度ロックオンを行わないようにする制御も必要になります。
A がすでにロックオンされている状態で、再度、マウスカーソルの位置を A に移動させてもロックオン済の場合にはロックオン対象として重複して追加を行いません
同じ対象に2回以上の攻撃は行わないようにしています。



 今回は敵のみを対象としてロックオンできるようにしますが、EventBase クラスを継承しているゲームオブジェクトであれば
どれでもロックオンできるようにすることが最終的な目標になります。
こうすることにより、敵、障害物、アイテムなど、すべての攻撃対象をロックオン可能になります。

 この部分は今回は実装していませんので、自分で検討してみてください。
設計だけではなく、クラスの継承などの学習にもなります。



 ロックオンした際には、ロックオンした対象の情報に加えて、ロックオンした位置の情報も保持するようにします。
この手法により、ロックオンした部分に弾の着弾エフェクトを生成させることが可能になります。

 これらの情報の保持の処理には、今までも学習してきている List の機能を活用するとよいでしょう。
ロックオン対象用の List、ロックオンした位置の保持用の List という風に
複数の List を用意しておいて、その中にロックオンのタイミングで情報を登録しておきます。

 攻撃ボタンを離した際には、これらの各 List の情報を元に、ロックオンした対象に対して弾を発射してダメージを与えたり、
ロックオンした位置に対して弾の着弾エフェクトを表示させることにより、ロックオン機能を完成させます。



 制御の内容は複雑になりますが、ロックオンに利用する処理や、List の処理などは、すべて学習済の内容になります。
そのため、いかにうまく各機能を組み合わせてロジックを構築し、それを実装していくことができるかがポイントになります。
'学習している処理の内容をしっかりと理解できているかどうかにより、設計ロジックを考えられるかどうかが変わります''。

 教材を見る前に、まずは自分でどのような処理を組み合わせていけば、ロックオンのロジックを構築することができるかを考えてみてください
イメージが浮かんだらそれをノートなどに書き出して、処理の繋がりを作ってみましょう

 設計力や実装力を養う訓練になりますので、難しい内容にはなりますが挑戦していただくことをお勧めします


2.TargetMarker ゲームオブジェクトを作成する


 ロックオンした対象が認識できるように、専用のマーカーをゲームオブジェクトを作成して、ロックオンした際に付与するようにします。

 ゲームオブジェクトは 2D Object の Sprite で作成しておくといいでしょう。
名称は TargetMarker にしてください。

 また、照準器の画像は無料素材をダウンロードし、Unity にインポートして利用するようにしてください。

 下記に参考例を提示します。


インスペクター画像



Scene ビュー画像



 製作物に正解はありません。自分の考えているイメージに沿ったものを実現するために、色々な方法を試してみてください。
自分の納得できる内容になるまで調整してください。


3.実際にゲームオブジェクトに付与してサイズや位置を調整する


 作成した TargetMarker ゲームオブジェクトですが、ゲーム内においては、ロックオンした対象の子オブジェクトとして配置して利用するように設計します。
こうすることにより、ロックオンした対象の移動に合わせて、TargetMarker ゲームオブジェクトも一緒に自動するようになるためです。
ただし、TargetMarker ゲームオブジェクトの向きについては調整が必要になります。これは次の手順でスクリプトを作成して制御します。



 それでは、敵用のゲームオブジェクトの子オブジェクトとして、作成した TargetMarker ゲームオブジェクトを配置してください。


ヒエラルキー画像



 続いて、サイズを調整し、見えやすい位置になるように、高さや奥行を調整します。
このときの情報を覚えておいてください。次の手順で利用します。


インスペクター画像



Scene ビュー画像



Game ビュー画像



 調整が完了したら、続いて、TargetMarker ゲームオブジェクトの制御を行うためのスクリプトを作成します。


4.TargetMarker スクリプトを作成する


 TargetMarker ゲームオブジェクトにアタッチし、制御を行うためのスクリプトになります。

 先ほどの手順で調整したサイズや位置を設定し、また、Update メソッドを利用して、常に TargetMarker ゲームオブジェクトが
カメラの方向に対して正面を向くように制御を行います。
カメラの方向を向く制御がないと、2D のゲームオブジェクトである TargetMarker ゲームオブジェクトがゲーム内で見えない角度が出来てしまうためです。


TargetMarker.cs

<= クリックすると開きます



*5.TargetMarker ゲームオブジェクトに TargetMarker スクリプトをアタッチしてプレファブにする


 作成した TargetMarker スクリプトを TargetMarker ゲームオブジェクトにアタッチしてください。
その後、プレファブにし、ヒエラルキーからは削除してください。

 プレファブにした TargetMarker ゲームオブジェクトを選択し、Position の値をすべて 0 に戻してください。
また、Scale の値もすべて 1 に戻してください。

 これらの情報は TargetMarker スクリプトによって制御を行います。


インスペクター画像



 以上で設定は完了です。


6.RayControlller スクリプトを修正する


 ロックオンの機能を追加します。

 まず、新しい攻撃用のボタンの登録を行うとともに、List や TargetMarker ゲームオブジェクトのプレファブを登録するための変数を追加します。

 処理の内容としては、新しい攻撃用のボタンを押し続けている間は、ロックオンの機能が有効になる処理を追加します。
この対象をロックオンする際の処理は、現在の攻撃の処理と同様に Raycast メソッドを活用します。
現在の攻撃では、Raycast メソッドで判定後、すぐに攻撃を行う流れになっていますが、この部分を List に保持しておくように変更しています。
同じく、ロックオンした位置についても List に保持しておきます。

 ロックオンの機能は、現在の弾数の値とロックオンした対象の数とを比べておくことで、現在の弾数を超えてはロックオンが機能しないように制御します。

 最後にロックオン用の攻撃用のボタンを離した際に、List にロックオンされた対象が登録されている場合には、
最初にロックオンされている対象から順番に攻撃の処理を1回ずつ行っていきます。これにより、3つの対象がロックオンされている場合には、
3回分の攻撃が行われて、着弾エフェクトの表示や、弾数の減少といった、通常の攻撃の処理と共通する制御を行います。

 この説明を読んだうえで、まずは、処理のイメージを作り、どのようなロジックになればこれらの一連の機能を実装することができるのか設計を考えてみてください。

 その後、実装に挑戦してから、最後にこちらのスクリプトを確認するようにしてみてください。


RayController.cs

<= クリックすると開きます。


 スクリプトを修正したらセーブします。


7.ゲームを実行して動作を確認する


 自分で書いた処理の見直しをして、処理の内容が説明できるか、スクリプト内に日本語でコメントを書いてみてください

 その後、処理の全体像を理解した上で、デバッグを行い、ロックオン機能がすべて実装されて正常に制御されているかを確認します。
事前に処理の見直しを行っているのは、どういった処理の制御が行われていれば正常であるのか、理解をした上でデバッグをおこなえるようにするためです。

 プログラムは書いた人が最後まで責任を持つ必要がありますので、書いて終わりではなくて、
自分の書いた処理がどのように動いてるのか、それは正常な制御なのか、間違っているのか、その判断をつけられるように
しっかりとスクリプトを読み解いていくようにしてください。

 ここでは確認項目については省略しますが、「ロックオン機能はすべて正常に動作している」と
自分で制御の良しあしの判断を付けてデバッグしてください


<実装動画>
動画ファイルへのリンク


 問題なくロックオンを実装することができたら、再度にリファクタリングにも挑戦してみてください。


8.リファクタリングに挑戦する


 現在の RayController スクリプト内には、重複して記述している処理があります。

 例えば、下記の処理です。

  // 発射エフェクトの表示。初回のみ生成し、2回目はオンオフで切り替える
  if (muzzleFlashObj == null) {
      // 発射口の位置に RayController ゲームオブジェクトを配置する
      muzzleFlashObj = Instantiate(EffectManager.instance.muzzleFlashPrefab, transform.position, transform.rotation);
      muzzleFlashObj.transform.SetParent(gameObject.transform);
      muzzleFlashObj.transform.localScale = muzzleFlashScale;

  } else {
      muzzleFlashObj.SetActive(true);
  }

 この処理は、2つのメソッド内に記述されており、その内容もまったく同じです。

 こういった処理は新しく1つのメソッドを作成し、現在の記述部分は新しく作ったメソッドの呼び出し命令に置き換えることで
重複していた処理を簡潔に記述することが出来ます。

 自分でも RayController スクリプトを見直してみて、重複している処理が見つかったら、それをメソッド化して1つにまとめることができないか、
検討した上で、処理を作り直してみてください。

 重複しているからすべてメソッド化する、ということではなくて、処理全体を見直したうえで、1つにまとめられそうな部分を見極めてから、
必要に応じてメソッド化していくことが大切です。

 こういった経験を通じて、スクリプトをリファクタリングする技術や、効率のよい設計を考えていく力を養うことが出来ます。

 是非取り組んでみてください。



 以上でこの手順は終了です。

 次は 発展27 ーゲーム時間機能の追加− です。