Unityに関連する記事です

 エネミーを List を利用して管理し、制御する処理を実装します。

<実装動画 ゲーム終了判定に伴い、画面に残っているエネミーとエネミーのバレットをすべて破壊する>
動画ファイルへのリンク


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

発展14 −Listの作成と活用−
28.EnemyGenerator スクリプトを修正し、生成したエネミーの情報をリストで管理する処理を追加する
29.GameManager スクリプトを修正し、ゲーム終了判定に伴い、画面に残っているエネミーとエネミーのバレットをすべて破壊する制御を追加する



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

 ・GameObject型以外のインスタンスの方法
 ・Listの使い方◆ Count プロパティ、Clear メソッド−



28.EnemyGenerator スクリプトを修正し、生成したエネミーの情報をリストで管理する処理を追加する

1.設計


 ゲーム終了となったときに、現在はずっとエネミーやエネミーのバレット、ヒット演出などのエフェクトが残っている状態です。
このうち、エネミーについては生成処理後にエネミーの管理は行っていないため、ゲーム内に何体のエネミーが残っているのかは
どのスクリプトも管理していない状態になっています。

 こちらの状態を改善し、エネミーを生成した段階で List にエネミーの情報を登録し、List を利用して管理できる処理を追加します。
管理ができるということは、「現在、ゲーム画面にいるエネミーが何体いるのか」だけではなく、「移動方法が Staraight のエネミーが何体いるのか」といった情報を
List の内容を検索することによって抽出する処理も実装可能になります。

 今回はこの List の情報を利用して、「ゲーム画面に残っているすべてのエネミーを破壊する」制御を追加します。
List にエネミーの情報を追加するにはエネミーを生成した段階で追加をする方法が最も簡便であるため、
必然的にこの List 用の変数や List に追加する処理は EnemyGenerator スクリプトに追加していく設計で考えます。

 List は宣言する際に型を自由に設定できます。そのため、エネミーの情報を GameObject 型で管理することもできますし、
エネミーのゲームオブジェクトにアタッチされている EnemyController 型で管理することも可能です。
何故ならばどちらも EnemySet ゲームオブジェクトを特定できる情報になっているからです。

 今回はエネミーのプレファブの指定と生成処理にも修正を行い、EnemyController 型の List を作成して管理します
EnemyController 型であれば、アタッチしているゲームオブジェクトを取得する場合も gameObject 変数を利用できるためすぐに特定可能です。

 逆の場合、つまり、GameObject 型でエネミーを管理する場合、もしもそのエネミーの情報をアタッチされている EnemyController スクリプトから取得したい場合には
その都度、GetComponent メソッドを実行してから情報を取得することになります。

 そういった処理の順番や1つの処理を行うまでの手間などを考えたときに、エネミーの情報を管理しつつ、アタッチされているゲームオブジェクトもすぐに取得できる
EnemyController 型で List を管理をしておいた方が使いやすく、便利であるためです。そのため、エネミーを生成する部分から見直しを行うようにします。

 変数の宣言1つをとってみても、その変数を利用する処理の前後を考えてから、どのような型にしておくべきか、を考えて設計しておくことが大切です



 List は削除命令がないかぎり、生成されたすべてのエネミーの情報が登録されますので、当然、プレイヤーによって破壊されたエネミーの情報も List には残り続けます。
そのため、安易に List の中身にあるエネミーをすべて破壊する、という処理を実行できません
なぜなら List にはゲーム内にエネミーが残っているかどうかは問わず、すべての生成されたエネミーを管理しているため、
破壊対象となるエネミーがない場合に対して Destroy メソッドを実行してしまうと、そこでゲームが停止してしまう不具合が発生するためです。
 
 この状態を改善するには、「エネミーが破壊されるたびに、List からそのエネミーの情報を削除するようにする」か、
あるいは、「List に残っているエネミーの情報のうち、ゲーム内に残っているエネミーだけを削除する」ようにするといった形で、処理に工夫が必要になります

 今回は2つ目の「List に残っているエネミーの情報のうち、ゲーム内に残っているエネミーだけを削除する」処理を実装します。
この場合、処理を追加するスクリプトが EnemyGenerator スクリプトのみで済む設計になるためです。
1つ目の処理がイメージできるのであれば、そちらで実装をしていただいても構いません。
その場合には、EnemyGenerator スクリプトと EnemyController スクリプトの両方に処理を追加することになります。考えてみてください。



 それに付随して、ボスの発射したバレットやエフェクトを管理している TemporaryObjectContainer ゲームオブジェクトも破壊する処理を追加します。
このゲームオブジェクトの子オブジェクトとしてバレットなどのゲームオブジェクトが管理されていますので、親オブジェクトである TemporaryObjectContainer ゲームオブジェクトが破壊されれば
その子オブジェクトであるすべてのゲームオブジェクトが一緒に破壊されます。エネミーのように1つずつ特定して消す必要がないため、今回はこの機能を活用します。


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


 設計に基づいて、EnemyController 型の List 変数の宣言を行います。
デバッグしやすいように、SerializeField 属性をつけて、ゲーム実行中にインスペクターより確認出来るようにしましょう。

 List が EnemyController 型であるため、エネミーのプレファブの型や、生成処理する処理も修正する必要があります
現在登録されているエネミー用のプレファブは GameObject 型で宣言されています。そのため、クローンされた時の情報も GameObject 型になります。
なぜならば、Instantiate メソッドの戻り値の型は、第1引数に指定している型がそのまま戻り値の型になりますので、
GameObject 型でエネミーのプレファブを登録しているため、それがそのまま戻り値の型になっています

 この第1引数に指定する型は GameObject 型以外に、生成したいゲームオブジェクトにアタッチされている自作クラス、つまり、今回であれば
EnemyController 型を指定して生成することが可能です。この場合にも、ゲーム内にはエネミーのゲームオブジェクトがちゃんと生成されます
ただし、戻り値の型が GameObject 型ではなく、EnemyController 型として取得できるようになります。この機能を利用して、戻り値を上手く活用します。

 List の型も EnemyController 型ですので、今回はこれを主軸としてロジックを組んでいきましょう


EnemyGenerator.cs

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


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



 EnemyGenerator ゲームオブジェクトのインスペクターを確認し、
新しく SerializeField 属性で宣言した変数が追加されて表示されていることを確認します。

 また、型を変更した enemySetPrefab 変数が None(未登録)の状態になっています。


EnemyGenerator ゲームオブジェクト インスペクター画像




3.<GameObject型以外のインスタンスの方法>

 
 Instantiate メソッドには戻り値があり、プレファブのゲームオブジェクトを設計図としてクローンの生成を行うとともに、生成に利用した型を戻り値として左辺へ戻します
そのため、GameObject 型でクローンの生成を行うと、GameObject 型が戻り値として戻されます

 この機能は GameObject 型には限らないため、クローンの生成を行いたいゲームオブジェクトに、自作したスクリプトがアタッチされている場合には
そのスクリプトを使って、クローンの生成を行うとともに、そのスクリプトの型を戻り値として戻すことが出来ます
この場合、左辺に用意する型もスクリプトの型を用意することで戻り値を受けとることが可能です。

GameObject型でのインスタンス処理(従来の実装方法)
    [SerializeField]
    private GameObject enemySetPrefab;    // クローンする際に利用するプレファブのゲームオブジェクト


    // プレファブからエネミーのクローンを生成する。生成位置は EnemyGenerator の位置。戻り値の値は GameObject 型になる(GameObject 型でインスタンスするので、戻ってくる型も GameObject 型)
    GameObject enemyObj = Instantiate(enemySetPrefab, transform, false);

  // エネミーのゲームオブジェクト(enemySetObj 変数の値)にアタッチされている EnemyController スクリプトの情報を取得して変数に代入
    EnemyController enemyController = enemySetObj.GetComponent<EnemyController>();

    // EnemyController スクリプトの SetUpEnemy メソッドを実行する => Start メソッドの代わりになる処理
    enemyController.SetUpEnemy(enemyData);


自作クラスでのインスタンス処理(新しく実装した方法)
    [SerializeField]
    private EnemyController enemySetPrefab;    // クローンする際に利用するプレファブのゲームオブジェクト


  // プレファブからエネミーのクローンを生成する。生成位置は EnemyGenerator の位置。戻り値の値は EnemyController 型になる
    EnemyController enemyController = Instantiate(enemySetPrefab, transform, false);    //  <=  ☆ 左辺に EnemyController 型の変数を用意して、インスタンスされたエネミーの情報を戻り値で受け取る

    // EnemyController スクリプトの SetUpEnemy メソッドを実行する => Start メソッドの代わりになる処理
    enemyController.SetUpEnemy(enemyData);


 違いとしては、プレファブとして登録する際の型や、インスタンス処理の際の左辺に用意する型が異なります

 そして最も大きな違いは、クラスの取得方法です。インスタンスされたゲームオブジェクトの持つクラスの情報を利用したい場合、
GameObject 型である場合には一度、GetComponetメソッドを利用して、操作を行いたいクラスの情報を取得する必要があります

 自作クラスでのインスタンスの場合、クラスとして生成されるため、GameObject 型の場合に必要な GetComponentメソッドの処理が不要になります。

 なぜかというと、自作クラスでインスタンス処理をした場合にはゲームオブジェクトのクローンを生成する部分は同じですが、
戻り値として EnemyController クラスを受け取っているため、GetComponent メソッドを実行せずとも、そのスクリプトの情報を自動的に左辺の変数に取得出来ています。

 このように GetComponent メソッド処理を省略する処理を書くことで、処理的に重い GetComponent 処理の負荷を減らすことが出来ます。
もしも生成したゲームオブジェクトのクローンに対して、何か処理を行いたいような場合には、GameObject型だけではなく、自作クラスにて生成することも念頭に置いて設計しておきましょう。


4.Listの使い方◆ Count プロパティ、Clear メソッド−


 新しく利用した List の機能について説明します。

1.Count プロパティ

 Count プロパティは List の持つプロパティの1つです。現在の List の要素の最大値を取得することができます。
配列では Length プロパティを利用して要素の最大値を取得できましたが、それと同じ使い方が出来ます。
今回は for 文の条件式に利用し、List の Count プロパティの値を目標値として繰り返し処理を行っています。

 List の要素も、配列と同じで [index] で指定してから利用する手順になります。

  // enemiesList の要素(中身)を1つずつ順番に、要素の最大値になるまで判定していく
  for (int i = 0; i < enemiesList.Count; i++) {

   // 要素が空ではない(プレイヤーに破壊されずにゲーム画面に残っている)なら
      if (enemiesList[i] != null) {

     // そのエネミーのゲームオブジェクトを破壊する
          Destroy(enemiesList[i].gameObject);
      }
  }

参考サイト
MicroSoft
List<T>.Count プロパティ
https://docs.microsoft.com/ja-jp/dotnet/api/system...


2.Clear メソッド

 List の持つメソッドの1つです。引数はありません。
Clear メソッドを利用すると、List の要素をすべて削除することが出来ます。実行すると、List の要素はなくなり、長さ(Count) は 0 になります。

 // リストをクリア(要素が何もない状態)にする
  enemiesList.Clear();

参考サイト
SamuraiBlog 様
【C#入門】Add、RemoveでListの要素を追加、削除する方法
https://www.sejuku.net/blog/41093


5.EnemyGenerator ゲームオブジェクトの設定を行う


 EnemyGenerator ゲームオブジェクトのインスペクターより、EnemyGenerator スクリプトを確認します。
型の宣言を変更した enemySetPrefab 変数が None (EnemyController) の状態になっています。

 括弧書きの部分がアサインした際に変数の情報として登録される型になりますので、
EnemyController スクリプトのアタッチされているゲームオブジェクトをドラッグアンドドロップしてアサインすることで登録できます。

 登録するゲームオブジェクトは前と同じです。Prefabs フォルダ内にある EnemySet ゲームオブジェクトを登録してください。
今までと異なるのは、GameObject 型での登録ではなく、EnemyController 型での登録になるという部分です。


EnemyGenerator ゲームオブジェクト インスペクター画像



 以上で設定は完了です。



29.GameManager スクリプトを修正し、ゲーム終了判定に伴い、画面に残っているエネミーとエネミーのバレットをすべて破壊する制御を追加する

1.設計


 EnemyGenerator スクリプトに生成したエネミーを管理するための List を用意し、
それを活用して、ゲーム内に残っているエネミーを破壊する処理をメソッドとして用意しました。
合わせて、一時オブジェクトを破壊するメソッドも用意しました。

 これらの処理を、ゲーム終了と判定されたときのタイミングで呼び出して実行することで、
残っているエネミーをすべて破壊し、一時オブジェクトにあるエネミーのバレットやヒット演出のエフェクトなどもまとめて破壊するようにします。

 今回の処理を実装する場所として TODO 機能を利用して処理をコメントしてあります。
こちらの部分に、上記の2つの処理のメソッドを実行する処理を追加して実装を完成させましょう。


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


 TODO の部分に、対象となる破壊処理のメソッドを呼び出す命令を実装します。
isGameUp 変数の値を上手く活用して処理を考えてみてください。


GameManager.cs

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


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


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


 すべての処理の実装が終了しましたので、処理を見なおしてから、ゲームを実行し挙動を確認します。

 ゲーム終了の判定は現在、拠点の耐久力が 0 以下になったときと、ボスを倒したときにゲーム終了と判定されています。
この両方の状態を発生させたときに、画面に残っているエネミーと、ボスのエネミーのバレット、そしてヒット演出用のエフェクトがすべて破壊されれば制御成功です。

 GameData ゲームオブジェクトより拠点の耐久力を設定できるようになっていますので、適宜調整してデバッグを行ってください。
繰り返し行うデバッグのために、DefenseBase ゲームオブジェクトではなく、GameData ゲームオブジェクトから値を変更可能にしています。
もしも自分で簡易化したい変数があった場合には、GameData スクリプトを修正して運用・管理するようにして、デバッグしやすい環境を作り出しましょう。


<実装動画 ゲーム終了判定に伴い、画面に残っているエネミーとエネミーのバレットをすべて破壊する>
動画ファイルへのリンク


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

 次は 発展15 ーフロート表示の作成ー です。

コメントをかく


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

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

Menu



技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

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

3D脱出ゲーム(抜粋)

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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