今回は3Dアクションゲームの作り方講座の第14回です。いよいよ完成まで近くなってきましたね。
前回は敵の遠距離攻撃を作成し、継承の使い方を学びました。
前回の記事:
第14回目は敵を自動出現させる機能、ボス戦を作り、ゲームステージを構築していきます。
ゲームステージの構想
まずはどういったステージにするのかを考えていきましょう。
今回は1ステージしかなく短いのでザコ敵が一定時間ごとに自動生成するようにして、一定数倒したらボスが出現。そのボスを倒すとゲームクリアとなるようにします。
ゲームとしてはやや単調で短いですが、こんなステージを複数個配置し、次々とクリアするようにしていくと立派な3Dスマホアクションゲームとしてリリースすることも可能です。
敵の自動生成処理の作り方
敵の出現機能を作る
まずは現在ただ置いてある敵をゲーム中に出現させるようにしましょう。
まず敵の死亡時処理を少し修正します。現在死亡時にはゲームオブジェクトを非アクティブにしていますが、これを破壊してしまいましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class EnemyBase : MonoBehaviour { ・・・ // ---------------------------------------------------------- /// <summary> /// 死亡アニメーション終了時コール. /// </summary> // ---------------------------------------------------------- void Anim_DieEnd() { Destroy( gameObject ); } ・・・ } |
一応再生して、敵を倒したらHierarchyから敵がなくなるか確認しておきましょう。
そして敵プレハブですが、もしここまでの設定をシーン上に配置されているオブジェクトに行っていた場合はプレハブではなくシーン上のオブジェクトのみにその設定が適用されプレハブには適用されていませんのでそれをプレハブに適用します。
どの様に確認をするかというと、シーン上の敵オブジェクトを選択しInspectorを見ましょう。下記画像のように、値の左側に青い線が入っている場合それはプレハブではなく配置されているオブジェクト独自に変更されています。
その場合はその値を右クリックして「Apply to Prefab ‘(プレハブ名)’」をクリックしましょう。するとシーン上の値がプレハブに適用されます。(Tagなども同様ですので忘れずに「Apply」しましょう。)
逆にシーン上のオブジェクトの値をプレハブの値に戻したい場合は「Revert」をクリックするとプレハブの値がシーン上のオブジェクトに適用されます。
二度手間感がありますが、次に再度「EnemyBase」を変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class EnemyBase : MonoBehaviour { ・・・ // 死亡時イベント. public EnemyMoveEvent DestroyEvent = new EnemyMoveEvent(); // ---------------------------------------------------------- /// <summary> /// 死亡アニメーション終了時コール. /// </summary> // ---------------------------------------------------------- void Anim_DieEnd() { // Destroy( gameObject ); // 確認のためコメントアウトしていますが削除してOKです。 DestroyEvent?.Invoke( this ); } ・・・ } |
まず「DestroyEvent」という「EnemyMoveEvent」を作成します。(移動時にも使用しているイベントなのでMoveイベントですがそのまま使います。気になる方はクラス名を変更しましょう。)
そして先程「Destory」した「Anim_DieEnd」の破壊処理を削除して、「DestroyEvent?.Invoke( this );」でイベントを実行します。この段階では死亡しても破棄も非アクティブもされなくなっていますのでご注意ください。
では次に「GameController」を編集していきます。追記と変更が必要になります。
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 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public class GameController : MonoBehaviour { ・・・ // 敵プレハブリスト. [SerializeField] List<GameObject> enemyPrefabList = new List<GameObject>(); // 敵出現地点リスト. [SerializeField] List<Transform> enemyGateList = new List<Transform>(); // フィールド上にいる敵リスト. List<EnemyBase> fieldEnemys = new List<EnemyBase>(); void Start() { ・・・ CreateEnemy(); CreateEnemy(); } // --------------------------------------------------------------------- /// <summary> /// 敵を作成. /// </summary> // --------------------------------------------------------------------- void CreateEnemy() { var num = Random.Range( 0, enemyPrefabList.Count ); var prefab = enemyPrefabList[ num ]; var posNum = Random.Range( 0, enemyGateList.Count ); var pos = enemyGateList[ posNum ]; var obj = Instantiate( prefab, pos.position, Quaternion.identity ); var enemy = obj.GetComponent<EnemyBase>(); enemy.ArrivalEvent.AddListener( EnemyMove ); enemy.DestroyEvent.AddListener( EnemyDestroy ); fieldEnemys.Add( enemy ); } ・・・ // --------------------------------------------------------------------- /// <summary> /// 敵破壊時のイベント. /// </summary> /// <param name="enemy"> 敵. </param> // --------------------------------------------------------------------- void EnemyDestroy( EnemyBase enemy ) { if( fieldEnemys.Contains( enemy ) == true ) { fieldEnemys.Remove( enemy ); } Destroy( enemy.gameObject ); } ・・・ } |
まずは追記部分のみです。
変数に「enemyPrefabList」と「enemyGateList」を「SerializeField」属性で作成します。それぞれ生成する敵のプレハブ、敵の出現位置を後ほどInspectorから設定します。
「fieldEnemys」は生成した敵を保管しておくためのListです。
「Start」関数内には後述の「CreateEnemy」を2回、これは適当に暫定的に実行しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void CreateEnemy() { var num = Random.Range( 0, enemyPrefabList.Count ); var prefab = enemyPrefabList[ num ]; var posNum = Random.Range( 0, enemyGateList.Count ); var pos = enemyGateList[ posNum ]; var obj = Instantiate( prefab, pos.position, Quaternion.identity ); var enemy = obj.GetComponent<EnemyBase>(); enemy.ArrivalEvent.AddListener( EnemyMove ); enemy.DestroyEvent.AddListener( EnemyDestroy ); fieldEnemys.Add( enemy ); } |
敵の生成処理です。「Random.Range」はランダムな値を取得する関数です。「num」「posNum」にそれぞれプレハブ、出現位置のリストの要素数を上限に値を取得し、そのインデックスの値のプレハブ、位置を保管します。
ランダムに決めた値から「Instantiate( prefab, pos.position, Quaternion.identity );」で敵オブジェクトを生成します。
その後、生成したオブジェクトから「EnemyBase」を取得し「ArrivalEvent」と「DestroyEvent」にそれぞれ、「EnemyMove」と「EnemyDestroy」(後述)を登録します。
1 2 3 4 5 6 7 8 |
void EnemyDestroy( EnemyBase enemy ) { if( fieldEnemys.Contains( enemy ) == true ) { fieldEnemys.Remove( enemy ); } Destroy( enemy.gameObject ); } |
最後に敵破壊時のイベントに登録する関数です。
「if( fieldEnemys.Contains( enemy ) == true )」というのは「fieldEnemys」に「enemy」が存在したら、という条件です。
存在する場合は「fieldEnemys.Remove( enemy );」でリストから「enemy」を削除します。
その後「Destroy( enemy.gameObject );」で敵を破棄します。
では、Unityに戻って出現位置を作成していきます。作成はほぼ移動位置と同じですので適当に5つほど「CreateEmpty」をして配置していきます。Yの値だけ低すぎず高すぎずにしておきましょう。(低いと埋まります、高いのは落ちてくるので多少はOKです)
「GameController」の「EnemyPrefabList」に「Slime Variant」「TurtleShell Variant」Projectフォルダからドラック&ドロップします。そして作成した、出現位置を「EnemyGateList」にHierarchyからドラック&ドロップします。
では一旦再生して確かめてみましょう。敵が2体生成され、攻撃し倒すとHierarchyからも消えていることを確認しましょう。
これで敵を生成する処理が完成しました。
ここで一旦「GameController」のいらない処理を削除していきます。もちろんそれによって処理がおかしくならない様に修正していきます。
削除する部分をコメントアウトして追加部分を記載、変更しない部分は省略しています。コメントアウト部分は確認後削除しましょう。
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
public class GameController : MonoBehaviour { ・・・ // enemysはすでに配置されている敵リストなので削除. // 敵リスト. // [SerializeField] List<EnemyBase> enemys = new List<EnemyBase>(); void Start() { ・・・ // foreach( var enemy in enemys ) // { // enemy.ArrivalEvent.AddListener( EnemyMove ); // } // テスト用なので削除. // CreateEnemy(); // CreateEnemy(); } // --------------------------------------------------------------------- /// <summary> /// 初期化処理. /// </summary> // --------------------------------------------------------------------- void Init() { Debug.Log( "初期化処理開始." ); } // --------------------------------------------------------------------- /// <summary> /// ゲームオーバー時にプレイヤーから呼ばれる. /// </summary> // --------------------------------------------------------------------- void OnGameOver() { ・・・ // 敵の攻撃フラグを解除. // foreach( EnemyBase enemy in enemys ) enemy.IsBattle = false; foreach( EnemyBase enemy in fieldEnemys ) enemy.IsBattle = false; } // --------------------------------------------------------------------- /// <summary> /// リトライボタンクリックコールバック. /// </summary> // --------------------------------------------------------------------- public void OnRetryButtonClicked() { ・・・ // 敵のリトライ処理. // foreach( EnemyBase enemy in enemys ) enemy.OnRetry(); // フィールド上の敵を削除しリストをリセット. foreach( EnemyBase enemy in fieldEnemys ) { Destroy( enemy.gameObject ); } fieldEnemys.Clear(); ・・・ Init(); } } |
「enemys」はあらかじめ配置されているフィールド上の敵のリストなので削除し、それを使用していた処理も同時に削除します。「OnGameOver」ではそのまま「fieldEnemys」に置き換えます。
「OnRetryButtonClicked」では、敵をリトライ処理するのではなくフィールド上の敵を一旦削除して「fieldEnemys」リストをクリアするようにします。
また「Init」という関数を追加(中身はまだなし)します。
敵を自動生成させる
次に暫定的に「Start」で作成していた処理を消したので、自動で生成されるようにしていきます。まずどの様な決まりのもとに生成するかを考えていきます。
無限に増え続けてもおかしいので、一定時間毎に生成してここでは10体まで増えたらそこで一旦止める形にしましょう。
今何体かというのは「GameController」の「fieldEnemys」の要素数を見ればわかるのでそれを元に作成していきます。
また敵を10倒したら生成を中断しボスを出現させます。では「GameController」を編集しましょう。
次回の記事:
コメント