前回(第5回)では、経路に沿って実際に敵オブジェクトが移動する仕組みなどを作りました。
前回の記事↓

今回は敵オブジェクトの作成を制御する仕組みを作っていきます。
Waveの概念も入ってきてあなたのレベルデザインの見せ所になります。では、作っていきましょう。
タワーディフェンスゲームでは一連の敵が出てくる流れ1セットをウェーブと呼びます。そして、決められた回数のウェーブを全て耐え、HPが0にならなければ勝ちです。
EnemyManagerの作成
敵オブジェクトを複数出現(生成)させていくにあたり、その生成タイミング等を管理するオブジェクト(EnemyManager)を作ります。
Hierarchyビューに、新規オブジェクト(CTRL+SHIFT+N)を作成し、新規ScriptもAddComponentします。
- 名前:EnemyManager
- スクリプト名:EnemyManager
では、EnemyManagerスクリプトを作成していきます。
このEnemyManagerに必要な要件としては
- 今が第何waveか管理 (
wave
) - waveがスタートしてからの経過時間を管理 (
time
) - 生成する敵(Enemy)が指定できる
- 生成する時間(Time)が指定できる
- Enemyが通る経路(Route)が指定できる
この辺りになります。
この、Time,Enemy,Route をワンセットで管理するEnemyPatternクラスと、1ウェーブは複数の敵の出現パターンで構成されるので、EnemyPatternをListで管理するWaveクラスをEnemyManagerクラスの外に書いておきます。
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 |
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyManager : MonoBehaviour { public Wave[] waves; public int wave; public float time; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } } [Serializable] public class Wave { public List<EnemyPattern> patterns; } [Serializable] public class EnemyPattern { public float time; public Enemy enemy; public Route route; } |
[Serializable]という、あまり見慣れないAttribute(アトリビュート)がありますが、これを書くことでInspectorに表示され、中身をUnityEditorで設定することが出来るようになる。と考えておくとよいです。興味がある方は試しにこの[Serializable]を外してみてください。Inspectorで表示されなくなります。
次に、実際に敵を生成する関数を作ります。
EnemyManagerスクリプトに以下のCreateEnemy関数を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public void CreateEnemy() { foreach (var pattern in waves[wave].patterns) { if (pattern.time <= time) { var enemy = Instantiate(pattern.enemy,new Vector3(-1000,-1000),Quaternion.identity); enemy.route = pattern.route; } } waves[wave].patterns.RemoveAll(pattern => pattern.time <= time); } |
この関数の中でやっていることはまず
3 |
foreach (var pattern in waves[wave].patterns) |
のforeachで現在のウェーブ(wave
)の敵パターンを全部patternに入れつつ回します。
5 |
if (pattern.time <= time) |
foreachの中のif文では、メンバ変数 time
の秒数から、生成されてなければいけない敵かどうかを判定しています。
7 8 |
var enemy = Instantiate(pattern.enemy,new Vector3(-1000,-1000),Quaternion.identity); enemy.route = pattern.route; |
生成の必要がある場合は、Instantiate でpatternにセットされているEnemyのprefabを生成(複製)した後、patternにセットされているRoute(経路)をセットして、生成したEnemyオブジェクトが経路に沿って移動する準備をしています。
なおInstantiate
で生成(複製)する際に生成位置(Vector3
)と回転(Quaternion
)を渡すことが出来るのですが、生成位置には new Vector3(-1000,-1000)
という、画面に絶対映らない凄い外を指定しています。これは生成した敵(Enemy)が画面中央に一瞬表示される事があったため予防策で指定しています。
回転(Quaternion
)は特に指定が無いので Quaternion.identity
という、無回転を意味するQuaternionを渡しています。
12 |
waves[wave].patterns.RemoveAll(pattern => pattern.time <= time); |
そして最後に、List の RemoveAllメソッドを使って既に生成したEnemyをもう一度作らないようにListから削除しています。
ただし、このCreateEnemy関数ですが、まだ誰からも呼ばれていません。
これでは確認ができないので、Update関数にテスト用の処理を書きましょう。なおあくまでもテスト用なので、この次回以降の講座のどこかで削除します。
1 2 3 4 5 6 7 |
// Update is called once per frame void Update() { wave = 0; time += Time.deltaTime; CreateEnemy(); } |
waveは取り合えず0固定で、経過時間はTime.deltaTime
(前回フレームからの経過時間)を足しこんでいきます。
以下が今回作ったEnemyManagerスクリプトのプログラム全体になります。
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 |
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyManager : MonoBehaviour { public Wave[] waves; public int wave; public float time; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { wave = 0; time += Time.deltaTime; CreateEnemy(); } public void CreateEnemy() { foreach (var pattern in waves[wave].patterns) { if (pattern.time <= time) { var enemy = Instantiate(pattern.enemy,new Vector3(-1000,-1000),Quaternion.identity); enemy.route = pattern.route; } } waves[wave].patterns.RemoveAll(pattern => pattern.time <= time); } } [Serializable] public class Wave { public List<EnemyPattern> patterns; } [Serializable] public class EnemyPattern { public float time; public Enemy enemy; public Route route; } |
※void Start(){ } は使用しないので削除してありますが、残っていても構いません。
忘れずにスクリプトの保存をし、UnityEditorに戻ります。
ちなみにUnityEditorで再生をしてみても何も動きません。それはそうです、まだWaveの中身を用意していないですからね。
では、Waveの中身を作っていきます。
Waveの作成
とりあえず、Waveは1つだけ作りましょう。 Size を 1 に変更します。
すると今度はWaveの中身、PatternsのSize が0です。これが、第1ウェーブで何匹の敵を出現させるか。という意味になります。とりあえず、これも 1 にしておいてください。
Patternsの中身は 先ほど用意した EnemyPattern クラスなので、EnemyPatternクラスのメンバ変数である、time,enemy,routeがさらにInspectorから指定できるようになっています。
とりあえず、適当なEnemyと、Routeをドラッグアンドドロップでセットします。(Timeは0のままでよいです)
ここで、注意なのですが、EnemyはAssets/Prefabs の中に作ってあるprefabを入れてください。(HierarchyビューのEnemy(青字)からドラッグアンドロップしないように)
これで、とりあえず指定した敵1体が0秒(開始と同時)に指定した経路で動き出すようになっているはずなんですが、Enemyを作った時にHierarchyに作ったEnemy,Enemy (1),Enemy (2) が最初からあると混同してしまって分かりづらいですね。その3つは今のうちに選択してDELETEキーで消してしまいましょう。
では、再生ボタンを押してみてください。
敵が生成されました!
敵パターン増産
このままでは1体しか敵が出てこない事になってしまうので、先ほど作った Patterns の Element 0 を右クリックして、Duplicate Array Elementを選択することで、複製が出来ます(この場合だとElement 1が作られます)
この要領で複製を繰り返して、Patterns の Size を 5 まで増やし、1秒間隔で出現するように、Timeが1ずつ増えていくように修正しましょう。
1秒間隔で、5体のEnemyが列になって動きました!
もちろん、同じEnemyのprefabだけじゃなくても構いませんし、Timeに小数点を入れても構いません。経路(Route)も好きに変更してみて、自分だけのウェーブを作ってみてください。コツは、ちょこちょこと再生をして、結果を確認する事です。
<例>
おさらいと次回予告
今回は敵を管理するEnemyManagerを作成し、Inspectorで敵の出現パターンを指定してウェーブが作れるようになりました。
次回は、この敵を倒す弓矢を作っていきます。
次回の記事↓

コメント