i-school - 戦車ゲーム ゲームクリアの条件を設計して実装する


設計

1.クリア条件を考える


 ゲームをクリアする条件を設計し、その条件を満たした際に、GameClear シーンへと遷移する処理を実装します。

 まずは GameClear シーンを作成しておきます。

 次に、どのようなクリア条件がよいかを考えて、そのロジックを設計します。
今回は、Main シーン内にいる Enemy タグを持つオブジェクトをすべて破壊したら、GameClear シーンへ遷移する、という条件とします。


2.クリア条件をロジック化し、必要な情報は何かを考える


 条件は決まりましたので、続いて、この条件のロジックを組むためには、どんな情報が必要となるかを何かを考えていきます。

 「Enemy タグを持つオブジェクトをすべて破壊したら」ということは「Enemy タグを持つオブジェクトがシーン内に0個になったら」と考えることが出来ます。
この条件を考えたとき、オブジェクトの情報を管理できていなければ条件式として組み立てることが出来ません。
つまり、ゲームシーン内に、Enemy というタグを持つオブジェクトが何個あるのかが分からなければ、この条件式は構築できないためです。

 Enemy タグを持つオブジェクトに関しては、以前に作成した、StopAttackItem スクリプトが役に立つでしょう。
この処理内では、タグを持つ複数のオブジェクトを探して配列に代入するという処理がありますので、それを応用してください。


<応用する>
  targets = GameObject.FindGameObjectsWithTag("EnemyShotShell");

 今回は条件を満たしたらシーン遷移を行うことが目的ですので、この敵の数を取得する処理は、シーン遷移処理が記述されているスクリプトへ追記するといいでしょう。
例えば、TankHealth スクリプトには、ゲームオーバーシーンへの遷移処理が記述されているため、using にシーン遷移用の宣言が追加されているので、このスクリプトを利用しましょう。


 オブジェクトの情報が取得できましたが、この情報は GameObject 型での取得になっています。
今回は何個あるのか、ということを数字として管理した方が管理しやすいので、この情報を元にして、敵の数を扱えるように新しく int 型の変数を用意します。
これも TankHealth スクリプトに宣言して用意しておきましょう。

 Enemy タグで取得した GameObject 型の配列の情報は、配列の最大数を取得することで、そのまま「敵の残りの数」と読み替えることが出来るためです。
これを int 型の変数に代入して、敵の数を把握出来るようにします。配列の長さの最大値は「Length」で取得出来ます


 あとは、この敵の数を、どのように管理をしていくかを考えていきます。
敵の数は、敵が破壊された際に減らしていくようにロジックを組めば、わかりやすく、処理がつくりやすいでしょう。

 ここでは、以前に学習したブロック崩しのロジックが応用できます。
ブロック崩しでは、ブロックが破壊されるたびに、GameMaster スクリプトが管理している BoxNum の数を1つ減らしていく処理を実装していました。
これを上手く利用しましょう。

 敵が破壊される処理は DestroyObjest スクリプト内に記述がありますので、ここに、ブロック崩しの Destroyer スクリプトを参考にしながら
敵の残数を減らしていく処理の追加を行います。


<ブロック崩しの Destroyer スクリプトを応用する>
  masterObj.GetComponent<GameMaster>().boxNum--;  <= これは、boxNum 変数が public 修飾子なので実行できます。

 考え方としては、同じような変数の設計にするか、あるいは、TankHealth スクリプトに敵の数を減らすメソッドを用意して呼び出すようにするか、です。
 

 

3.処理を順番に実装する


 スクリプトに処理を記述する際には、日本語のコメントを事前に書いておいて、それを元にプログラムを書いていく手法がおすすめです。
ロジックを構築しやすいですし、コメントがそのまま残り、処理の書き洩らしを防ぐことが出来るためです。


実装手順


 設計を元に、以下の手順で実装を行います。まずは自分でロジックを考えて実装に挑戦してみてください。
また、ここにある処理は必要最低限の処理になりますので、自分なりにアレンジを自由に行ってください。

 この実装例では、敵の数の管理は private 修飾子の変数で行うように設計しています。
そのため敵が破壊された際に、TankHealth スクリプトに用意したメソッドを呼び出すことによって、この敵の残り数を減算する処理でロジックを組んでいます。

 1.GameClearシーンを作成する
 2.TankHealth スクリプトを修正して、敵の残り数を管理し、カウントするための処理を追加する
 3.DestroyObject スクリプトを修正して、敵が破壊された際に TankHealth の CalculateEnemyCount メソッドを呼び出す処理を追加する
 4.ゲームを実行して動作を確認する


1.GameClear シーンを作成する

1.新しいシーンを作成する


 Scenes フォルダ内で右クリックをしてメニューを開き、Create => Scene を選択します。
新しく Scene が作成されますので、名前を GameClear(あるいはそれに類似する名称)に変更します。
ダブルクリックして、GameClear シーンを開いて編集を行います。

<手順動画 新しいSceneを作成>
https://gyazo.com/5e1efa2d65c4a70be5fffc55f0009882

フォルダ画像



2.GameClear表示を作成する


 Canvas を新しく作成して、Text、あるいは Image を持つゲームオブジェクトを追加し、ゲームクリアの表示を作成します。

 Imageで作成する場合には、ゲームクリアの文字のロゴを作成し、それを Image コンポーネントの Sourece Image に設定を行います。

 Textで作成する場合には、Text プロパティ欄に "GameClear" と入力しておきます。

 どちらの方法で実装していただいても構いません。また他の装飾(BGMの再生、背景画像の設置など)も自由に行ってください。

 今回の例では Text にて文字として GameClear を表示させています。
 

<参考画像>



3.Build Settings に GameClear シーンを追加する


 最後に左上のメニューから File => Build Settings を選択してBuild Settings ウインドウを開いて、Build in Scenes に GameClear シーンを追加します。
一番最後に追加しておきましょう。


Build Settings ウインドウ画像



2.TankHealth スクリプトを修正して、敵の残り数を管理し、カウントするための処理を追加する


 クリア条件のロジックを構築するために必要な変数を1つ追加します。
敵の数(残りの数)を管理するために、int 型の enemyCount 変数を用意します。
この値には、Start メソッド内でEnemy タグを持つ敵を配列で取得した後に配列の長さ(最大値)を代入します。
何故ならば、この配列は敵の情報が代入されており、敵の数分だけ要素を持っているからです。

 またこの enemyCount 変数は private 修飾子であるため、外部のスクリプトからは直接参照して増減処理を行うことが出来ません。
外部のスクリプトから呼び出してもらうために public の CalculateEnemyCount メソッドを用意して、減算を行う際にはこのメソッドを利用してもらうようにします。

 このメソッド内では enemyCount 変数の減算後に、敵の数がクリアの条件を満たしているか = enemyCount 変数の値が 0 かどうかを判定するようにしていますので、
敵の数のカウントダウンとクリアの判定を一緒に行うようにロジックを組んでいます。


TankHealth.cs

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



3.DestroyObject スクリプトを修正して、敵が破壊された際に TankHealth の CalculateEnemyCount メソッドを呼び出す処理を追加する


 OnTiriggerEnter メソッドの最後に処理を追加しています。
どのような処理になっているのか、記述しながらコメントを書いて理解を深めましょう。


DestroyObject.cs

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



4.ゲームを実行して確認する


 以上で実装は終了です。ゲームを実行して、クリアの条件を満たす処理が正常に制御されているかを確認します。

 まずは enemyCount の値が正常に代入されたかを、Console ビューで確認を行います。
TankHealth スクリプトの Start メソッドに Debug.Log メソッドを書いてありますので、ここでヒエラルキーにいる Enemy タグを持つ敵の数と
この enemyCount の値が同じかどうかを確認します。
今回の例ではヒエラルキーにEnemy タグを持つゲームオブジェクトが2つありますので、Console にも 2 体と表示されています。
(自分のプロジェクトにある Enemy タグを持つゲームオブジェクトの数で確認しましょう)


Console ビュー画像




 そのまま Enemy タグを持つ敵のゲームオブジェクトを破壊してみます。
DestroyObjest スクリプトの OnTiriggerEnter メソッド内に記述した、TankHealth スクリプトの CalculateEnemyCount メソッドの呼び出しが実行されて
Console ビューに 敵の数が更新された表示がでれば成功です。


Console ビュー画像



<検証動画 Enemy タグを持つオブジェクトを破壊すると、EnemyCount が減算される>
https://gyazo.com/ac26c64a50c94ca550c35124c4aec408



 壁を破壊してみましょう。
このゲームオブジェクトのタグは Enemy ではありませんので、同じ DestroyObjest スクリプトがアタッチされていますが制御されているので
TankHealth スクリプトの CalculateEnemyCount メソッドの呼び出しは行われません。


Enemy タグを持たないオブジェクトを破壊しても、EnemyCount は減算されない
https://gyazo.com/9919113d35a7658f1a3152e349eb46de



 残った敵をすべて破壊して、敵の数を 0 にしたときに、自動的に GameClear シーンへ遷移すれば成功です。
Console にも GameClear の表示が出ます。


Console ビュー画像



Enemy タグを持つすべてのオブジェクトを破壊すると GameClear シーンへ遷移する
https://gyazo.com/cdfd3c87ea36a7f879a7904513cf24ea


 以上でこの手順は終了です。おつかれさまでした。