Unityに関連する記事です

 プレファブにした敵キャラと移動経路のゲームオブジェクトを利用して、敵の自動生成処理を実装します。
この処理が実装できれば、次は、敵の移動経路をランダム化したり、移動経路の情報を矢印で表示したりする機能も実装できるようになります。

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


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

手順13 ー敵キャラの自動生成処理の実装ー
22.敵の自動生成処理を1つのクラスにて管理・生成までの処理を実装する
23.処理の内容を管理クラスと生成クラスに分けて、自動生成の処理を実装する



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

 ・自分でロジックを考えて実装してみる
 ・GameObject型以外のインスタンスの方法
 ・役割に応じたクラスの分担方法



22.敵の自動生成処理を1つのクラスにて管理・生成までの処理を実装する

1.設計


 敵の自動生成にあたり、生成の管理を行うクラス(スクリプト)と、生成処理を行うクラス(スクリプト)とを分けて考えるようにします。

 生成の管理を行う側を GameManager クラス、生成処理を行うクラスを EnemyGenerator クラスとしてまずはイメージします。
これらのクラスに役割の分担と、どのような関連性によって成り立つようにするかを設計していきます。

<GameManager クラス>
 ・敵の生成の管理を行う → 敵が生成可能な状態かどうかを判断し、生成された敵の数や破壊された敵の数を管理する
 ・他にも、敵の生成するための待機時間の設定や、生成する敵の最大数なども管理する
 ・ひいてはゲームの管理も行うようにしていく

<EnemyGenerator クラス>
 ・敵の生成を行う → 一定時間が経過するごとに、敵が生成可能な状態かどうかを GameManager クラスより判断し、敵の生成を行う
 ・敵の生成が可能な状態とは、生成する敵の最大数に達しておらず、指定された待機時間が経過しているか判定を行う

 この2つの関連性をロジックとして考えて、2つのクラスを順番に作成していきます。
いきなり2つ作成しても構いませんが、まずは EnemyGenerator クラスのみを作成し、この中で生成までの待機時間や敵の最大生成数などを一時的に設定しておきます。

 EnemyGenerator クラスの処理が正常に動作するようになってから、GameManager クラスを作成して、生成までの待機時間や敵の最大生成数などを設定し、
その情報を EnemyGenerator クラスで運用するように設計を変更します。

 これをすべて1回でイメージできるのであれば、両方のクラスを作成しても問題ありません。
ですがロジックのイメージがわかない場合には、まずは1つのクラス内ですべての処理を実装し、その後、役割を分担していく方向でクラスを作っていきましょう。


2.EnemyGenerator スクリプトを作成する


 敵の自動生成にかかわる処理を実装します。
まずは自動生成に必要な情報をすべてこのクラス内に記述し、その後、管理クラスへと移管します。

 自動生成の機能を実装するためには、どのような情報が必要になるか、まずは検討してみてください。
時間の経過により生成するのであれば、時間の経過をカウントする変数と、目標となる時間を設定する変数が必要になります。

 いつまでも敵を生成しつづけてもよいですが、多くのタワーディフェンスゲームには敵を生成する上限値があります。
上限値があって、それで管理をするということは、敵の生成を行った現在値の情報も必要になります。
比較対象がないと上限値に達しているかどうかを判断できないためです。

 こういった部分を検討しながら変数の準備を行い、そしてこれらを利用してロジックを考えてみてください。
そのほかにも必要に応じて、役割を持たせた変数を準備しましょう。

 敵キャラの情報と移動経路の情報 Prefabs フォルダにある Enemy ゲームオブジェクトと PathData ゲームオブジェクトを利用することを考えてください。
それらを GameObject 型で宣言するのではなく、それぞれのゲームオブジェクトにアタッチされているクラス(スクリプト)で宣言しておくことで
GetComponent メソッドの命令を省略できます。


EnemyGenerator.cs


 スクリプトを作成したらセーブをします。


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

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

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

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


    // プレファブからエネミーのクローンを生成する。戻り値の値は GameObject 型になる(GameObject 型でインスタンスするので、戻ってくる型も GameObject 型)
    GameObject enemyObj = Instantiate(enemyPrefab, pathData.generateTran.position, Quaternion.identity);

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

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


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


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

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


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

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

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

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

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


4.EnemyGenerator ゲームオブジェクトを作成し、EnemyGenerator スクリプトをアタッチして設定を行う


 ヒエラルキーの空いている場所で右クリックをしてメニューを開き、Create Empty を選択します。
新しいゲームオブジェクトが作成されますので、名前を EnemyGenerator に変更します。

 先ほど作成した EnemyGenerator スクリプトをドラッグアンドドロップしてアタッチします。
スクリプトをアタッチしたので、必ずゲームオブジェクトにアタッチさせているか、インスペクターを見て確認を行います。
続けて、EnemyGenerator スクリプトの設定を行います。


インスペクター画像



 SerializeField属性 と public 修飾子にて宣言されている変数が合計6つありますので、それを順番に設定します。

 enemyControllerPrefab 変数には、Prefabs フォルダにある Enemy ゲームオブジェクトをドラッグアンドドロップしてアサインします。
EnemyGenerator スクリプトの情報が自動的に登録されます。

 enemyGenerateTran 変数には Prefabs フォルダにある PathTranSet ゲームオブジェクトをドラッグアンドドロップしてアサインします。
PathData スクリプトの情報が自動的に登録されます。PathData スクリプトに設定している GenerateTran 変数の情報を敵の生成位置として利用します。

 isEnemyGenerate 変数は敵の生成を許可するための変数です。Start メソッド内で true に設定しますので、ここでは設定は不要です。
チェックを外したままにしておいてください。

 generateIntervalTime 変数は敵の生成までの待機時間です。初期値として 100 を設定しておきます。

 generateEnemyCount 変数は敵の生成した数のカウント用です。こちらは初期値のままで構いませんので 0 にしておきます。
敵を生成するたびに自動的に 1 ずつカウントアップするように制御しています。

 maxEnemyCount 変数は敵を生成する最大値の設定値です。初期値として 5 を設定しておきます。
この数になるまで、敵が生成されるように制御しています。


インスペクター画像



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


 設定した内容に基づいて敵の自動生成が機能するかを、ゲームを実行して確認してみましょう。


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


 次の手順では、生成における処理の内容を管理・生成に分けて考えて、それぞれの役割によってクラスを分けて作成します。


23.処理の内容を管理クラスと生成クラスに分けて、自動生成の処理を実装する

1.設計


 EnemyGenerator クラスが完成し、自動生成の処理が実装できました。
こちらの制御について、管理と生成とに分けて処理のリファクタリングを行うにはどうすればいいのかを考えてみます。

<管理>
1.敵の生成の許可/終了の判定値を制御し、その情報を生成側に共有する
2.敵の生成数と最大数の管理を行い、最大数に達した場合には敵の生成の終了を判定する
3.敵の自動生成までの待機時間などの設定値も管理する

<生成>
1.管理側の敵の生成許可と終了の情報を利用して、許可されている間は敵の自動生成処理を行う
2.管理側の敵の生成が終了の判定となったら自動生成の処理を終了する
3.時間の経過については生成側で行い、自動生成の待機時間については管理側から情報を共有してもらい利用する

 以上のような役割分担の方法が考えられます。
よって EnemyGenerator スクリプト内の処理を、上記の内容に合わせて書き替えるとともに、新しく GameManager スクリプトを作成して、
EnemyGenerator スクリプト内の変数や処理を移管します。

 処理の流れについてはロジックを考える際にはフローチャートを自分で作って考えてみると処理の可視化が出来るので分かりやすくなります。


2.GameManager スクリプトを作成する


 いままでは EnemyGenerator スクリプトが Start メソッドを活用して、自分で生成の許可を出して、生成準備用のメソッドを実行していました。
そのままでは管理できていることになりませんので、この一連の流れを GameManager スクリプトが制御するようにします。

GameManager クラス側で EnemyGenerator クラスの生成準備のメソッドを実行する流れを考えてみてください。
生成準備のメソッドでは while 文によるループ処理が実装されていますので、この条件についても、管理側である GameManager クラスが管理して制御することを考えてみます。

 現在の敵の生成数の管理と、上限値に達した場合に生成を終了する判定を行う処理についても、GameManager クラス側で行うようにします。

 このような設計構造にすることにより、EnemyGenerator クラスは命令を受けて敵の生成を行う役割のクラスになります。
生成の開始や終了についてはすべて GameManager クラス側で制御できるようにしています。(トップダウン式の構造です。)


GameManager.cs


 スクリプトを作成したらセーブを行います。


3.GameManager ゲームオブジェクトを作成し、GameManager スクリプトをアタッチして設定を行う


 ヒエラルキーの空いている場所で右クリックをしてメニューを開き、Create Empty を選択します。
新しいゲームオブジェクトが作成されますので、名前を GameManager に変更します。

 先ほど作成した GameManager スクリプトをドラッグアンドドロップしてアタッチします。
スクリプトをアタッチしたので、必ずゲームオブジェクトにアタッチさせているか、インスペクターを見て確認を行います。
続けて、GameManager スクリプトの設定を行います。

 enemyGenerator 変数には、ヒエラルキーにある EnemyGenerator ゲームオブジェクトをドラッグアンドドロップしてアサインします。
EnemyGenerator スクリプトが自動的に登録されます。

 他4つの変数は EnemyGenerator スクリプトより移管したものになりますので、EnemyGenerator と同じように設定をおこなってください。


インスペクター画像



 以上で設定は完了です。


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


 今回はわかりやすく、Public 修飾子で宣言している4つの変数を GameManager クラス側に移管していますので、
それらの変数をすべて削除します。また、Start メソッドの内容もそのまま移していますので、こちらも削除します。

 PreparateEnemyGenerate メソッドが GameManager クラス側で実行されて、GameManager クラスを引数として渡してくれていますので、
それを受け取れるように引数に GameManager 型の情報の宣言を追加します。この情報を EnemyGenerator クラス内で活用できるように
新しく GameManager 型の変数を宣言フィールドに追加しておいて、それを代入して利用できるようにします。

 また次回以降に実装する処理のイメージがあるばあい、それを TODO として記述しておくことで、処理の全体像を把握しやすくします。


EnemyGenerator.cs


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


5.EnemyGenerator ゲームオブジェクトの確認をする


 EnemyGenerator ゲームオブジェクトのインスペクターを確認し、変数が削除されていることを確認します。
また残っている変数のアサインが外れていないかも一緒に確認しておきます。

インスペクター画像



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


 役割を分担してクラスを修正しただけですので、処理の機能自体は変わりません。
そのため、ゲームを実行して今までと同じように敵の自動生成を行い、上限値に達したら生成が終了すれば制御成功です。


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




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

 次は 手順14 −敵キャラの経路表示の自動生成処理の実装− です。

コメントをかく


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

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

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

3D脱出ゲーム(抜粋)

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

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

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

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

3Dトップビューアクション(白猫風)

VideoPlayer イベント連動の実装例

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

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

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

private



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

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