Unityに関連する記事です

 エネミーが一定の間隔でバレットを発射してくるようになりましたので、それに付随する処理を追加します。
耐久力の値を減算するようにしたり、エネミーのバレットがプレイヤー目掛けて発射されるようにします。


<実装動画 .痢璽泪襪離┘優漾爾琉貮瑤妊廛譽ぅ筺爾諒向に向かってバレットを発射させる>
動画ファイルへのリンク


<実装動画◆.椒垢皀廛譽ぅ筺爾諒向に向かってバレットを発射させる>
動画ファイルへのリンク


 以下の内容で順番に実装を進めていきます。

発展13 ーバレット関連の追加処理の実装−
25.エネミー用のバレットが拠点に侵入した際に耐久力を減算する処理を追加する
26.エネミーのバレットの発射する方向を真っすぐ下方向から、プレイヤーのいる方向へ発射するように修正する
27.ボスもバレットを発射できるようにする



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

 ・共通する処理を複数かかないように考えて記述する
 ・戻り値と引数を利用した処理の実装例



25.エネミー用のバレットが拠点に侵入した際に耐久力を減算する処理を追加する

1.設計


 エネミーからバレットが自動生成されるようになりましたので、次は、エネミーのバレットが拠点に侵入した際に耐久力の値を減算する処理を実装します。
実際に自分でゲームを作る際も、全体の設計を考えてから、1つずつ順番に処理を実装していくようにしましょう。

<設計内容>
〇1.特定のエネミーが一定の間隔でバレットを自動生成し、エネミーから真っすぐ下方向へ発射する
◇2.エネミーのバレットに接触した際に、拠点の耐久力を減算する処理を追加する
 3.エネミーのバレットの発射する方向を真っすぐ下方向から、プレイヤーのいる方向へ発射するように修正する
 4.バレットの親子関係を変更する

 今回の実装は【◇2】の部分になります。



 耐久力の値に関する処理の修正になりますので、記述が必要になるのは DefenseBase スクリプトになります。

 エネミーのバレットにアタッチされているコライダーを元に、侵入判定をおこないます。
すでに OnTriggerEnter2D メソッドがありますので、その中に処理を記述します。

 エネミーのバレットも、エネミー本体と同じで Enemy という Tag が設定されています。
同じ Enemy の Tag で2つのゲームオブジェクトがある場合には、そのゲームオブジェクトがエネミーのバレットなのか、あるいはエネミー本体であるのか、
どのような制御を行えば分岐できるか考えてみてください。

 また、制御後の内容は、耐久力の値を減算する値以外は同じ処理になります。
そういった場合、同じ処理を書かずに分岐が必要な部分にだけ分岐制御を行い、あとは共通する処理を実行するようにできるようにしてください。

<ロジックの流れ>
 1.Enemy の Tag を持つコライダーが侵入した
      ↓ 
 2.エネミーのバレットか、エネミー本体なのかで分岐し、それぞれの持つ攻撃力分だけ耐久力の値を減算する
      ↓
 3.ここから共通の処理。耐久力を更新し、ゲームオーバーの判定を行い、エフェクトを生成し、侵入したゲームオブジェクトを破壊する

 
 2と3の処理の部分に修正が必要になります。
3の部分を共通して利用するにはどのように修正すればよいか、OnTriggerEnter2D メソッド内の処理と、UpdateDurability メソッドの引数に注目して修正を考えてください。


2.DefenseBase スクリプトを修正する


 設計に基づいて、DefenseBase スクリプトを修正し、ロジックを記述してください。


DefenseBase.cs

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


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


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


 処理を実装したらゲームを実行して制御できるようなっているか挙動を確認します。
エネミーのバレットが拠点に侵入した場合と、エネミー本体が拠点に侵入した場合に、耐久力の値が減算するようになっていれば制御成功です。
Debug.Log メソッドもありますので、Console ビューでも処理の流れを確認してください。


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


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


 無事に実装が完成したら、次の手順に進みましょう。
普通にゲームとして遊んでみてください。エネミーのバレットでの攻撃があるだけで臨場感が増していることが分かります。


26.エネミーのバレットの発射する方向を真っすぐ下方向から、プレイヤーのいる方向へ発射するように修正する

1.設計


 この手順では、エネミーのバレットを真っすぐ下方向ではなく、プレイヤーのいる位置に向かって発射するように制御を変更します。

<設計内容>
〇1.特定のエネミーが一定の間隔でバレットを自動生成し、エネミーから真っすぐ下方向へ発射する
〇2.エネミーのバレットに接触した際に、拠点の耐久力を減算する処理を追加する
◇3.エネミーのバレットの発射する方向を真っすぐ下方向から、プレイヤーのいる方向へ発射するように修正する
 4.バレットの親子関係を変更する



 プレイヤーのいる位置、というのは、PlayerController スクリプトや、それがアタッチされている PlayerSet ゲームオブジェクトの情報を取得することで利用できるようになります。
この情報が取得できれば、この位置とエネミーの位置とを計算することによって「方向」を算出することが可能になります。
この方向は Vector3 型の情報になりますので、それをバレットの引数として渡すことができれば、現在は下方向に固定している発射の方向を、プレイヤーのいる位置に向かって発射出来るようになります。

 現在、PlayerSet ゲームオブジェクトの情報を変数として管理しているスクリプトはありません。
代わりに、PlayerController スクリプトを変数として管理し、扱えるようにしているスクリプトは、GameManager スクリプトがあります。

 設計上、考えていただきたいポイントとしては、プレイヤーのいる位置の情報を取得して利用したいのは
バレットを発射する EnemyController スクリプトです。ただし、エネミーには、バレットを発射するタイプと、発射しないタイプがいます。

 つまり、EnemyController スクリプトとして PlayerController スクリプトの情報を取得するには、利用範囲が限定されるため、
なるべくならば、変数は用意せずに、外部のスクリプトより情報を取得して利用をすることを検討したい部分です。

 使いたい情報があるからスクリプトで取得する、という考え方自体は問題ありませんが、少し設計に慣れてきたり、
スクリプト全体を見渡して設計を行うという視点で検討をしていただくようになると、ワンステップ進んだ設計が可能になります。



 今回はこの設計の理念に基づいて、EnemyController スクリプトでは PlayerController スクリプトを変数や検索して取得はせずに、
この PlayerController スクリプトの情報を管理している GameManager スクリプトから情報を受け取って利用する設計にします。

 取得する方法は、PlayerController スクリプトを直接引数で取得するだけではなく、戻り値を利用することでも取得できます。
とくに今回はPlayerController スクリプトそのものをずっと利用する訳ではなく、エネミーの位置とプレイヤーのいる位置の情報を使って「方向」を計算したい、というケースです。
ずっとプレイヤーのいる位置情報を管理することは必要ありませんので、バレットを発射する際に、プレイヤーのいる位置の情報とエネミーの位置から「方向」が計算できれば問題ない訳です。

 以上のことから、GameManager スクリプトには、エネミーの位置情報を受け取って、自分の管理している PlayerController スクリプトの位置情報を利用して計算し、
算出された「方向」の情報を EnemyController スクリプトへと戻すような処理を実装します。

 EnemyController スクリプトは直接 GameManager スクリプトへの参照を変数として管理していませんので、
間に EnemyGenerator スクリプトに入ってもらって、EnemyGenerator スクリプト経由で GameManager スクリプトにある「方向」を計算する処理を実行してもらいます。

<ロジックの流れ>
 1.GameManager スクリプトに、エネミーの位置情報を受け取って PlayerController スクリプトの位置情報と差分を計算し、「方向」として算出した結果を戻り値として戻すメソッドを用意する
 2.EnemyGenerator スクリプトに、EnemyController スクリプトよりエネミーの位置情報を受け取って GameManager スクリプトに用意された「方向」を計算するメソッドを呼び出し、戻り値として「方向」の情報を受け取る
 3.EnemyController スクリプトに EnemyGenerator スクリプトに用意されたメソッドを呼び出し、GameManager スクリプトで処理されて取得した「方向」の情報を受け取る
 4.受け取った方向の情報を Bullet スクリプトの引数として渡し、プレイヤーのいる位置に向かってバレットを発射する

 以上のような流れになります。

 1つずつ処理を記述しながら読み解いてください。
今回は戻り値を持つメソッドを2回続けて呼び出して利用していますので、しっかりと処理を理解しましょう。


2.GameManager スクリプトを修正する


 設計に基づいて、必要な処理を記述していきましょう。
このスクリプトには、EnemyGenerator スクリプトより呼び出して実行してもらうメソッドを準備することになります。
そのメソッド内でプレイヤーの位置情報とエネミーの位置情報を計算し、「方向」を算出して、処理の結果を呼び出し元に戻すように考えてください。

 プレイヤーの位置情報は PlayerController 型の変数から参照できますが、GameManager スクリプトはエネミー1体ごとの位置情報は管理していません
こういった場合には、メソッドに引数を用意してエネミーの位置情報を受け取り利用するようにロジックを考えてください。
そうすれば、必要なエネミーの情報を使って、プレイヤーの位置情報と計算することが出来ます。


GameManager.cs

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


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


3.EnemyGenerator スクリプトを修正する


 こちらも設計に基づいて、必要なメソッドを追加してください。

 必要な処理は、EnemyController スクリプトより呼び出してもらって実行するメソッドになります。
この処理の中で、GameManager スクリプトに用意したメソッドを呼び出すようにロジックを組んでください。

 GameManager スクリプトに用意したメソッドは引数として、エネミーの位置情報を要求しています。
そのため、この EnemyGenerator スクリプトに用意するメソッドも、EnemyController スクリプトから呼び出す際に
エネミーの位置情報を要求するようにしてください。そうすることで、スクリプトとスクリプトを経由して
エネミーの位置情報を GameManager スクリプトまで渡して届けることが可能になります。


EnemyGenerator.cs

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


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


4.EnemyController スクリプトを修正する


 設計に基づいて、EnemyGenerator スクリプトに用意したメソッドを呼び出す処理を記述してください。
引数に注意して処理を考えてください。


EnemyController.cs


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


5.処理を読み解く


 3つのスクリプトにまたがった一連の処理がどのように実行されて、どのような情報が取得できるようになっているのかを読み解きましょう。
1.EnemyController スクリプトの EnemyShot メソッドが実行されて、EnemyGenerator スクリプトにある PreparateGetPlayerDirection メソッドが実行される
  // エネミーのバレットのクローンを生成し、Bullet スクリプトを取得して、ShotBullet メソッドを実行する
  Instantiate(enemyBulletPrefab, transform).GetComponent<Bullet>().ShotBullet(enemyGenerator.PreparateGetPlayerDirection(transform.position));

 上記の処理の中で、ShotBullet メソッドの引数に、Vector3 型の戻り値を持つ EnemyGenerator スクリプトにある PreparateGetPlayerDirection メソッドを実行しています。
このメソッドの引数には、エネミーの位置情報を指定して渡しています。

 メソッド処理を引数の中に記述できる理由は、ShotBullet メソッドの引数の指定が Vector3型であり、引数で実行するメソッドの戻り値が同じ Vector3 型であるためです。

  // エネミーのバレットのクローンを生成し、Bullet スクリプトを取得して、ShotBullet メソッドを実行する
  Instantiate(enemyBulletPrefab, transform).GetComponent<Bullet>(). ;

 このような処理になっています。

 実行したメソッドの引数に戻り値の持つメソッドの指定がある場合、それは、戻り値を持つメソッドの処理がすべて終了してからメソッドが実行されます
今回のケースで言えば、ShotBullet メソッドは、引数に指定している PreparateGetPlayerDirection メソッドの処理の結果を待って(戻り値が取得できて)から実行されることになります。
そのため、ちゃんと Vector3 型の情報を ShotBullet メソッドに渡すことが出来ています。


2.EnemyGenerator スクリプトの PreparateGetPlayerDirection が実行されて、GameManager スクリプトにある GetPlayerDirection メソッドが実行される
    /// <summary>
    /// プレイヤーとエネミーとの位置から方向を判定する準備
    /// </summary>
    /// <returns></returns>
    public Vector3 PreparateGetPlayerDirection(Vector3 enemyPos) {
        return gameManager.GetPlayerDirection(enemyPos);   // 呼び出しているメソッドの処理結果を EnemyController スクリプトの ShotBullet メソッドの引数として戻している
    }

 こちらのメソッド内でも、GameManager スクリプトに用意されている、戻り値を持つ GetPlayerDirection メソッドが実行されています。
そのため、この戻り値を持つメソッドが解決されて始めて、EnemyController スクリプトの最初に実行している ShotBullet メソッドの戻り値が確定します。


3.GameManager スクリプトの GetPlayerDirection メソッドが実行される
    /// <summary>
    /// プレイヤーとエネミーとの位置から方向を判定
    /// </summary>
    /// <returns></returns>
    public Vector3 GetPlayerDirection(Vector3 enemyPos) {
        return (playerController.transform.position - enemyPos).normalized;  // この処理結果が EnemyGenerator スクリプトの PreparateGetPlayerDirection メソッドに戻る
    }

 このメソッド内では、引数として、EnemyController スクリプト → EnemyGenerator スクリプト → GameManager スクリプトと経由して
EnemyController スクリプトからエネミーの位置情報が Vector3 型で届いています。この値と PlayerController 型の変数にある PlayerSet ゲームオブジェクトの位置情報を計算し、
算出された差分の値を normalized 変数を利用して速度ベクトルに変換しています。速度ベクトルについては、プレイヤーのバレットの部分で説明していますので復習しておいてください。
それを最終的な値として、このメソッドを呼び出している EnemyGenerator スクリプトの PreparateGetPlayerDirection メソッドに戻り値として戻しています。
 
 EnemyGenerator スクリプトの PreparateGetPlayerDirection メソッドも戻り値をもっていますので、
GetPlayerDirection メソッドの結果を さらに EnemyController スクリプトの ShotBullet メソッドの引数として戻しています。


<メソッドと戻り値の型による処理の流れ>
 ShotBullet(enemyGenerator.PreparateGetPlayerDirection(transform.position)) → PreparateGetPlayerDirection → GetPlayerDirection

 ShotBullet(Vector3) → 戻り値[Vector3 型] PreparateGetPlayerDirection → 戻り値[Vector3 型]  GetPlayerDirection

 そして各メソッドの戻り値によって、元の処理に遡って値が戻っていきます

<戻り値の流れ>
  このメソッドの処理結果(方向の情報)を前のメソッドに戻す
 戻り値[Vector3 型]  GetPlayerDirection
   ↓  
 ⊂綉のメソッドの処理結果が PreparateGetPlayerDirection メソッドに戻ってくる = このメソッドに「方向」の情報が届いている
 戻り値[Vector3 型] PreparateGetPlayerDirection
   ↓  
 上記のメソッドの処理結果が ShotBullet メソッドの引数に戻ってくる = 引数に「方向」の情報が届いている
 ShotBullet(Vector3 型)

 以上のような処理の結果によって、EnemyController スクリプトの処理が GameManager スクリプトのメソッドで計算されて、
最終的に ShotBullet メソッドまで値が戻ってきて値が取得できていることが分かります。
 

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


 以上の処理の流れをイメージしながら、実際に処理が動いているかを確認していきます。
エネミーがバレットを発射した際に、真っすぐ下方向ではなく、プレイヤーのいる位置に向かって発射するようになっていれば制御成功です。

 戻り値の処理の場合は途中で処理が実行できない場合には最初の処理である ShotBullet メソッドも実行できなくなります。


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


 処理を読み返して、流れをつかんでおいてください。


27.ボスもバレットを発射できるようにする

1.設計


 最後に残っている手順を設計して実装を行います。

<設計内容>
〇1.特定のエネミーが一定の間隔でバレットを自動生成し、エネミーから真っすぐ下方向へ発射する
〇2.エネミーのバレットに接触した際に、拠点の耐久力を減算する処理を追加する
〇3.エネミーのバレットの発射する方向を真っすぐ下方向から、プレイヤーのいる方向へ発射するように修正する
◇4.バレットの親子関係を変更する

 まずは、ボスがバレットを発射できるようにしましょう。

 現在は移動方法が Straight のエネミーのみバレットを発射していますので、この条件に Boss_Horizontal の移動方法も追加してください。
ゲームを実行し、ボスもバレットを発射するようになっているか、ゲームを実行して確認してください。

 何回か実行して試していると、不自然な点があることが分かります。

<検証動画>
動画ファイルへのリンク



 ボスが左右の方向を変更するタイミングで、バレットの方向が一緒に追従して変更してしまうことが分かります。
これでは想定している挙動になりません。発射したバレットは、そのままプレイヤーのいる位置に向かって発射されることが目標です。

 この部分を修正していきます。

 この処理の原因は、バレットを生成した際に、エネミーを親として生成するようになっているため、
子オブジェクトであるバレットは親のオブジェクトの移動位置に追従することにより、バレットの位置も一緒に変化してしまうことです。

 以前、親子関係ではエフェクトでも同じ不具合がありました。
エネミーが破壊されてしまうと、その子オブジェクトとして生成されたエフェクトも破壊されてしまっていた問題です。

 今回も親子関係での問題ですので、バレットを生成し、発射する部分まで処理が終了したら、親子関係を解消し、
そういったゲームオブジェクトを格納するための TemporaryObjectContainer ゲームオブジェクトを親として再構築するようにしましょう。

 気を付けてなければならないのは記述する処理の順番です。
発射してから親子関係を変更しないと、親子関係を変更した時点でバレットの位置が変更になってしまうため、正常にエネミーの位置から発射できなくなります。


2.EnemyController スクリプトを修正する


 設計に基づいて処理のロジックを考えて実装をしていきます。
親子関係を構築する際の処理は以前にも実装している DefenseBase スクリプトの処理を参考してください。

 また今まではバレットをインスタンスと同時に Bullet スクリプトを取得して ShotBullet メソッドを実行していましたが、
この場合、インスタンスされたゲームオブジェクトの情報がどこにもないため、そのままでは親子関係を構築できません。
 
 再度ゲームオブジェクトを探す所から始めるのでは大変ですので、Instantiate メソッドの戻り値を利用して生成されたバレットを新しく用意した同型の変数に代入し、
その変数を利用して、ShotBullet メソッドを実行してから、親子関係を構築するように処理のロジックを書き換えてください。


EnemyController.cs


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


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


 実装が終了したらゲームを実行して、ボスのエネミーのバレットの挙動を確認してください。
エネミーが左右にターンする際でも発射したバレットの位置が変更されることなく、プレイヤーの方向に向かって動いてくれば制御成功です。


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


 以上でこの手順は終了です。非常に難しい手順であったと思いますので、しっかりと復習して処理の流れの理解を深めてください。
戻り値については、これらの処理を参考にしながら、仮のプログラムを書いてどのように動くかを確認しておくといいでしょう。

 自分で考えて作った処理の方が、教材よりも何倍も理解できます。処理を読んで書く、の繰り返しが大切です。


 次は 発展14 −Listの作成と活用− です。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます