本講座では、UnityとC#を用いて3DFPSゲームを作成しています。今回は第10回です。
前回までで敵の攻撃処理を作成しました。アニメーション・当たり判定・遠距離攻撃も作成したので、敵の一連の行動AIはできあがりです。
前回の記事:
次にこの敵を指定した位置に自動で配置できるようにしていきます。
FPSゲームでは倒すべき敵を指定した位置にスポーン・リスポーンする機能が必須です。今回は一人プレイのFPSゲームですが、マルチプレイに対応する際にも役立つ知識になります。
では始めます。
Unityで敵を指定した位置に自動生成・スポーンさせるための準備
まずは、新しくゲーム全体の管理のための「AppGameContorller」というスクリプトを「Assets/AppMain/Script」に作成します。
敵の生成を制御するC#スクリプトの作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AppGameController : MonoBehaviour { // 敵の出現ポイント. [SerializeField] List<Transform> enemySpawnPoints = new List<Transform>(); // 地上の敵プレハブ. [SerializeField] GameObject enemyPrefab = null; // 空の敵プレハブ. [SerializeField] GameObject skyEnemyPrefab = null; // フィールド上の敵総数. [SerializeField] int enemyCount = 20; // 現在フィールドにいる敵. List<EnemyBase> currentFieldEnemies = new List<EnemyBase>(); // 減った敵を再生成するフラグ. bool isEnemyRespawn = false; void Start() { } void Update() { } } |
先に変数を準備しておきます。
「enemySpawnPoints」という「List<Transform>」を作成します。これは現在「EnemyCrocodile」にあるリストをこちらに持ってきます。その修正はまた後ほど行います。
次の「enemyPrefab」「skyEnemyPrefab」は生成する敵プレハブです。これはここまでに生成した敵をプレハブ化していきます。「enemyCount」はフィールド上に生成する敵の総数です。
ここまで全て「SerializeField」にしておきましょう。
また「currentFieldEnemies」と「isEnemyRespawn」を「private(省略)」で用意します。これらは後ほど使用します。
プレハブ、出現(移動)位置の作成
Inspectorで変数に必要なものを設定していきます。
最初にHierarchyに空オブジェクトを作成し、「AppGameController」という名前にしておきます。そして作成したスクリプト「AppGameController」をアタッチしておきましょう。(位置角度はなんでも構いませんが、Position、Rotationは全て0、Scaleは全て1にしておくといいでしょう)
では、現在Hierarcyにある「Enemy1」と「EnemyBat」を、Projectウインドウの「Assets/AppMain/Prefab」にドラック&ドロップしてプレハブ化します。
できたらHierarchyのゲームオブジェクトは消しておきましょう。
次に以前作成した「EnemyMovePoints」の子にある「Goal1」(名前は違っても敵の移動目的地になっているもの)を複製してまずは16個くらいにします(何個でもOKです)。
その16個をどこでも構いませんが、今回は下記画像の一周している道の周辺あたりに配置します。
その時位置によって高さが違いますので、各ゴールは地面の近くに来るように調整しておきます。(下記画像の白い棒は解説用の目印なのでなくて構いません)
Inspector設定
これらを「AppGameController」に設定します。
変数「EnemyPrefab」に「Enemy1」のプレハブを、変数「skyEnemyPrefab」に「EnemyBat」のプレハブを、「EnemySpawnPoints」には「EnemyMovePoints」以下の「Goal」を面倒ですが一つずつ設定します。
EnemyBaseの調整
敵処理の修正をしていきます。
まずは「EnemyBase」。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public class EnemyBase : MonoBehaviour { ・・・ // 最大HP. // [SerializeField] protected int maxHp = 3; protected int maxHp = 3; ・・・ // ゲームコントローラー. protected AppGameController gameController = null; // 攻撃力. protected float attack = 1f; protected virtual void Start() { // Init(); 削除. } // --------------------------------------------------------------- /// <summary> /// 初期化処理. /// </summary> // --------------------------------------------------------------- public virtual void Init( AppGameController appGameController, int maxHp, float attack ) { this.maxHp = maxHp; this.attack = attack; hp = maxHp; gameController = appGameController; } ・・・ // --------------------------------------------------------------- /// <summary> /// 死亡時処理. /// </summary> // --------------------------------------------------------------- protected virtual void OnDead() { Debug.Log( gameObject.name + "を倒しました" ); isDead = true; gameController.OnDeadEnemy( this ); } } |
最初に変数にある「maxHp」の「SerializeField」を消します。これはスクリプトから生成するため、その時に設定するように変更するためです。そして新しく「gameController」、「attack」を作成します。
次に「Start()」関数にある「Init()」関数の実行を削除します。
続いて「Init()」関数の引数に「AppGameController appGameController」、「int maxHp」、「float attack」を追加します。
処理では「gameController = appGameController」を追加し「AppGameController」を取得しておき、「this.maxHp(変数のmaxHp)」に「maxHp(引数)」を「this.attack(変数)」に「attack(引数)」を保管しておきます。
このように生成時の初期化で「HP」と「攻撃力」を設定できるようにして、強さに変化を持たせることができるようにしておきます。
また「OnDead()」関数には「gameController.OnDeadEnemy( this )」を追加しますが、この関数は少し先に作成するのでこの段階ではエラーになりますのでご注意ください。
煩わしい方は一旦コメントアウトして後で戻してください。忘れそうな方はエラーのままにしておけばUnityに戻った時にエラーログがでて思い出せます。
AttackSphereの調整
「AttackSphere」を調整します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class AttackSphere : MonoBehaviour { ・・・ // 攻撃力. //[SerializeField] float attack = 1f; float attack = 1f; ・・・ // --------------------------------------------------------------- /// <summary> /// 初期化処理. /// </summary> // --------------------------------------------------------------- public void Init( float attack ) { cor = StartCoroutine( AutoDestory() ); this.attack = attack; } ・・・ } |
こちらは簡単です。変数の「attack」の「SerializeField」をなくします。
そして「Init()」関数の引数に「float attack」を追加し、処理にも先ほどと同じように「this.attack = attack」で保管しておきます。
EnemyCrocodileの調整
次に「EnemyCrocodile」の変更をします。
スポーン・リスポーン機能の最終確認とまとめ
では最後に確認です。
まずは敵が20体生成されます。その後敵を倒すと足りない分だけ生成され、また20体に戻ることを確認してみてください。
敵が早く生成され過ぎるように感じるかもしれないので、その場合はyield return new WaitForSeconds(1f); の部分を遅らせるとよいでしょう。
また、int num = Random.Range(0, 2);のあたりで敵の種類を2と定義していますが、マジックナンバーになってしまうので今後このゲームを改造していく際は「フィールドで生成される敵の種類の最大値」を別途変数などに格納しておき、その値を用いる方が良いですね。
練習と思ってリファクタリングしてみるのも良いと思います。
ひとまずこれで敵が無限に湧き続けるスポーン・リスポーン処理が完成しました。
今はワニの敵は「HP3攻撃1」、コウモリは「HP1攻撃1」で固定して生成していますが、例えば敵を倒した数を測定しだんだん強くなるような設定も、この引数で値を設定してやることでできるようになります。
今回はここまでにしましょう。
次回はゲームルールや、UIなど詳細が必要になってきますのでその辺りを作成していきましょう。
次回の記事 :
コメント