1. はじめに
Unityで2Dタワーディフェンスゲームを作るシリーズのパート4へようこそ!
ここまでで、敵の配置、移動、攻撃システム、HP管理 などを実装してきましたね。
今回は、ゲームのパフォーマンスを向上させるためのオブジェクトプールの作成、敵のスポーン管理、ウェーブシステム、そして拠点の体力システムを実装していきます。
この記事で学べること
この記事では、以下の内容を学べます。
✅ オブジェクトプールの作成方法(敵・弾の再利用)
✅ 敵のスポーンシステムの実装(一定間隔 / ランダム)
✅ ウェーブ管理の実装(敵の出現を管理し、全滅後に次のウェーブを開始)
✅ 拠点の体力の実装(敵がゴールに到達したら体力が減少)
この機能を追加することで、ゲームのパフォーマンスが向上し、スムーズなプレイ体験が可能になります!
必要な準備(前回の記事の復習)
今回の内容に進む前に、パート1〜3で実装した機能を振り返っておきましょう。
🟢 パート1:敵の配置とアニメーション
- 敵オブジェクト(Enemy) を作成
- HPバーの実装(ダメージを受けるとゲージが減少)
- アニメーションの設定(歩行・被弾エフェクトの作成)
🟢 パート2:武器と弾の発射
- 武器オブジェクト(FireWeapon) を作成
- 弾の発射&ターゲット追尾 を実装
- リロードシステムの追加(一定間隔で弾を撃てるように調整)
🟢 パート3:敵の移動とアニメーション
- 移動ポイント(MovePoint) の設定
- 敵が順番にポイントを移動するシステムの作成
- 被弾時のアニメーション再生&移動停止処理
💡 今回のパート4では、これらの要素をさらに発展させ、敵の出現を制御し、ゲームのシステムを強化していきます!
準備ができたら、敵のオブジェクトプールの作成 から始めていきましょう! 🎮💡
2. 敵のオブジェクトプールの作成
タワーディフェンスゲームでは、多くの敵が登場し、消えては再出現することになります。
しかし、敵を毎回Instantiate(生成)し、Destroy(削除)するとパフォーマンスが低下 してしまいます。
そこで、オブジェクトプール を使い、敵をあらかじめ作成して非表示にし、再利用する ことでパフォーマンスを最適化します。
① Systemフォルダに ObjectPooler スクリプトを作成
まず、Systemフォルダ を作成し、その中に 「ObjectPooler」スクリプト を作成します。
このスクリプトは、敵オブジェクトを管理し、必要に応じて使い回せるようにするものです。
以下のスクリプトを入力してください。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
//生成するアイテム
[SerializeField] private GameObject poolObject;
//生成する数
[SerializeField] private int poolSize = 10;
//生成したオブジェクトを管理するリスト
private List<GameObject> pool;
//親オブジェクトを格納しておく
private GameObject poolContainer;
private void Awake()
{
//インスタンス化
pool= new List<GameObject>();
//オブジェクトを生成して名前を付けて変数に格納
poolContainer = new GameObject($"Pool - {poolObject.name}");
//プールオブジェクトの作成
CreatePooler();
}
/// <summary>
/// poolSizeの数poolObjectを生成する
/// </summary>
private void CreatePooler()
{
//poolsizeの分ループ
for (int i = 0; i < poolSize; i++)
{
//生成したオブジェクトをリストに格納
pool.Add(CreateObject());
}
}
/// <summary>
/// poolobjectを生成して呼んだ場所に返す
/// </summary>
/// <returns></returns>
private GameObject CreateObject()
{
//オブジェクトを作成して変数に格納する
GameObject newInstance = Instantiate(poolObject);
//親の設定
newInstance.transform.SetParent(poolContainer.transform);
//非表示
newInstance.SetActive(false);
//返す
return newInstance;
}
/// <summary>
/// プールからオブジェクトを引き出す
/// </summary>
/// <returns></returns>
public GameObject GetObjectFromPool()
{
//リストに格納されている分ループする
for (int i = 0; i < pool.Count; i++)
{
//もしプール内の非表示オブジェクトだったら
if (!pool[i].activeInHierarchy)
{
//呼び出した場所に返す
return pool[i];
}
}
//足りなければ生成して返す
return CreateObject();
}
/// <summary>
/// プールにオブジェクトを返却
/// </summary>
/// <param name="instance"></param>
public static void ReturnToPool(GameObject instance)
{
instance.SetActive(false);
}
}
② スクリプトの解説
この ObjectPooler
スクリプトでは、10体の敵オブジェクトをあらかじめ生成し、非表示にしておきます。
敵が必要になったときは、プールから非表示のオブジェクトを取り出して再利用し、倒されたらまたプールに戻します。
- GetObjectFromPool() で未使用の敵を取得
- ReturnToPool() で倒された敵を非表示にしてプールへ戻す
この方法を使うことで、敵の生成・削除の負荷を大幅に軽減 できます。
③ 敵のプレハブ化
次に、敵オブジェクトをプレハブ化 します。
- Hierarchy ウィンドウ で
Enemy
オブジェクトを作成 - 必要なコンポーネント(
SpriteRenderer
、Rigidbody2D
など)を追加 - プレハブ化する(
Project
ウィンドウにドラッグ&ドロップ) - Hierarchy にある
Enemy
を削除(プレハブのみ残す)
④ Waypoint_and_Spawner に ObjectPooler をアタッチ
次に、敵を管理する Waypoint_and_Spawner
(元々 MovePoint
)オブジェクトに ObjectPoolerスクリプトをアタッチ します。
Waypoint_and_Spawner
を選択- Inspector の「Add Component」 から
ObjectPooler
を追加 - 「PoolObject」欄に、先ほど作成した
Enemy
のプレハブを設定

💡 これで Waypoint_and_Spawner
が 敵の生成・管理を担当するオブジェクト になりました!
⑤ エディタでEnemyが生成されているか確認
- ゲームを再生(▶ボタンをクリック)
Hierarchy
にPool - Enemy
というオブジェクトが生成されることを確認Pool - Enemy
の中に 非表示の敵オブジェクトが 10 体入っていることをチェック

もし敵が生成されない場合は、以下の点を確認してください。
✅ ObjectPooler
スクリプトが Waypoint_and_Spawner
にアタッチされているか
✅ PoolObject
に Enemy
プレハブが正しく設定されているか
✅ poolSize
が 0 になっていないか

これで、敵のオブジェクトプール を作成し、必要に応じて敵を生成・再利用する仕組み が完成しました!
次は、武器と弾のオブジェクトプール を作成し、弾の発射処理を最適化 していきます。 🎯🚀
3. 武器のオブジェクトプールの作成
前のセクションで、敵のオブジェクトプール を作成し、パフォーマンスの最適化を行いました。
次に、弾(Bullet)もオブジェクトプールで管理し、発射&回収をスムーズにする仕組み を作ります。
① FireWeapon に ObjectPooler をアタッチ
まず、武器 (FireWeapon
) に ObjectPooler スクリプトをアタッチ し、弾をオブジェクトプールで管理 できるようにします。
- Hierarchy ウィンドウ で
FireWeapon
オブジェクトを選択 - Inspector の「Add Component」 から
ObjectPooler
を追加 - 「PoolObject」欄に、プレハブ化した
Bullet
を設定
✅ これで FireWeapon
は弾を管理するオブジェクトになります!
② WeaponControlスクリプトにオブジェクトプール対応のコードを追加
次に、WeaponControl
スクリプトを編集し、弾をInstantiate(生成)するのではなく、オブジェクトプールから取得 するように修正します。
🔹 コード修正:オブジェクトプールを利用
//ObjectPoolerを格納するための変数
private ObjectPooler pooler;
//スタート関数に書く
pooler= GetComponent<ObjectPooler>();
//ReloadBulletに書く
GameObject newBullet = pooler.GetObjectFromPool();//GameObject newBullet = Instantiate(fireBullet);を消して書き直す
//表示
newBullet.SetActive(true);
✅ これで弾を毎回生成せず、オブジェクトプールから取得して発射するようになりました!
③ Bulletスクリプトにオブジェクトプール対応のコードを追加
次に、Bullet
スクリプトを修正し、弾が消えたらDestroy(削除)するのではなく、オブジェクトプールに戻す ようにします。
🔹 コード修正:弾の発射後の処理
🔹 コード修正:弾の初期化
//CheckDistanceのif文に書く
//プールに戻す
ObjectPooler.ReturnToPool(gameObject);//destroyは消す
//BulletInitializationに書く
ResetBullet();
private void ResetBullet()
{
enemyTarget = null;
transform.localRotation= Quaternion.identity;
}
✅ これで弾は消えず、オブジェクトプールに戻り再利用されるようになりました!
④ Bulletの発射&回収をエディタで確認
- ゲームを再生(▶ボタンをクリック)
FireWeapon
から 弾が発射されるか確認- 敵に当たると、弾が プールに戻って非表示になるか確認
- しばらくすると、再び 弾が発射されるか確認
🔹 チェックポイント ✅ Bullet
がオブジェクトプールから取得されているか?
✅ 敵に当たると Bullet
が Destroy
されず、非表示になっているか?
✅ 次の弾が発射されたとき、前の弾が再利用されているか?
💡 もし弾が発射されない場合は、以下を確認してください。
FireWeapon
に ObjectPooler が正しくアタッチされているか?PoolObject
に Bulletプレハブ が設定されているか?CheckDistance()
でObjectPooler.ReturnToPool(gameObject);
が正しく呼ばれているか?
まとめ
✅ FireWeapon
に ObjectPooler
を追加し、弾を管理するように設定
✅ WeaponControl
を修正し、弾をオブジェクトプールから取得
✅ Bullet
を修正し、消えたらオブジェクトプールに戻す
✅ エディタで弾の発射&回収を確認

これで、弾の無駄な生成・削除をなくし、ゲームのパフォーマンスを向上 させることができました!🎯🚀次は、敵のスポーンシステムの実装 に進みます!
4. 敵のスポーン管理
タワーディフェンスゲームでは、一定間隔またはランダムなタイミングで敵を出現させる 必要があります。
ここでは、敵をオブジェクトプールから取得し、スポーンさせるシステム を実装していきます!
① Spawnerスクリプトを作成し、Waypoint_and_Spawnerにアタッチ
まず、敵をスポーンさせる管理スクリプト Spawner
を作成し、Waypoint_and_Spawner
にアタッチします。
System
フォルダ内に 「Spawner」スクリプト を作成Waypoint_and_Spawner
オブジェクトにSpawner
をアタッチ
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//スポーンモード
public enum Spawnmodes
{
constant,//一定間隔でスポーンする
Random,//ランダムにスポーンする
}
public class Spawner : MonoBehaviour
{
//スポーンモードの選択
[SerializeField] private Spawnmodes spawnModes = Spawnmodes.constant;
//最短のスポーン間隔
[SerializeField] private float minRandomDelay;
//最長のスポーン間隔
[SerializeField]private float maxRandomDelay;
//一定モードのスポーン時間
[SerializeField] private float constantSpawnTime;
//スポーンさせる数を設定する
[SerializeField] private int enemyCount = 10;
//タイマー変数
private float spawnTimer;
//スポーンさせた数(数を追加していく)
private float spawned;
//enemyのオブジェクトプール用
private ObjectPooler pooler;
//MovePoint格納用 Enemyに渡すためにここに格納する
private Movepoint movePoint;
private void Start()
{
//変数にコンポーネントを格納する
pooler = GetComponent<ObjectPooler>();
movePoint= GetComponent<Movepoint>();
}
private void Update()
{
//spawnタイマーの時間を減らす
spawnTimer -= Time.deltaTime;
//確認
if (spawnTimer < 0)
{
spawnTimer = GetSpawnDelay();
//スポーン上限の確認
if (spawned < enemyCount)
{
//生成済みの数を追加
spawned++;
//敵を生成
SpawnEnemy();
}
}
}
/// <summary>
/// 敵を生成する
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void SpawnEnemy()
{
//プールから取得して変数に格納
GameObject newInstance = pooler.GetObjectFromPool();
//エネミーの初期設定
SetEnemy(newInstance);
//表示する
newInstance.SetActive(true);
}
private void SetEnemy(GameObject newInstance)
{
//enemyでMovepointを使えるように格納している
Enemy enemy = newInstance.GetComponent<Enemy>();
enemy.movepoint = movePoint;
//生成位置をこのオブジェクトの位置に設定
enemy.transform.position = transform.position;
//スピードの設定
enemy.SetMoveSpeed();
}
/// <summary>
/// 一定もしくはランダムの数値を返す
/// </summary>
/// <returns></returns>
private float GetSpawnDelay()
{
if (spawnModes == Spawnmodes.constant)
{
return constantSpawnTime;
}
else
{
//引数の間からランダムな数値を選んで返す
return UnityEngine.Random.Range(minRandomDelay, maxRandomDelay);
}
}
}
この Spawner
スクリプトは、敵(Enemy)を一定間隔またはランダムな間隔でスポーンさせる ためのスクリプトです。
オブジェクトプール (ObjectPooler
) を利用し、敵の生成・管理を効率化 しています。
- Inspector でスポーン設定を行えるようにする

これで Waypoint_and_Spawner
が敵のスポーンを管理するオブジェクト になりました!
② エディタで敵が一定間隔でスポーンするか確認
- ゲームを再生(▶ボタンをクリック)
Waypoint_and_Spawner
から敵が 設定した間隔でスポーンするか確認- 敵の数が
enemyCount
に達すると、それ以上スポーンしないことを確認 - スポーンモード(一定間隔 / ランダム)を変更し、動作をチェック
🔹 チェックポイント ✅ 敵が一定間隔またはランダムな間隔で出現するか?
✅ スポーン数が enemyCount
を超えないか?
✅ 敵が Spawner
の位置からスタートして適切に移動するか?
💡 もし敵が出現しない場合は、以下を確認してください。
Spawner
にObjectPooler
が正しくアタッチされているか?PoolObject
にEnemy
プレハブが設定されているか?spawnModes
の設定が適切か?
まとめ
✅ Spawner
を作成し、Waypoint_and_Spawner
にアタッチ
✅ スポーンモード(一定間隔 / ランダム)を設定
✅ スポーン数を制限し、敵の出現を管理
✅ 敵の初期設定を行い、適切な位置でスタート
✅ エディタで動作を確認し、敵が正しくスポーンすることをチェック
スポーンシステムをより簡単に管理したいなら?
本記事では、スクリプトを使って敵のスポーンやウェーブ管理を実装しましたが、もっと手軽にタワーディフェンスのシステムを構築したいなら、「Tower Defense Toolkit 4 (TDTK-4)」がおすすめです!
このアセットを使えば、ウェーブ管理やスポーンシステムがすぐに導入でき、複雑なスクリプトを書かなくてもタワーディフェンスの基本機能を整えられます。

次は、敵が最終地点に到達したときの処理を実装 し、ゴール後の挙動を制御していきます!🎯🔥
5. 最終地点に到着した敵の処理
敵が移動ルートの最後に到達したときの処理を実装します。
通常、タワーディフェンスゲームでは 敵が拠点に到達したらHPを減らし、敵を削除する 仕組みが必要です。
しかし、毎回 Destroy()
するのではなく、オブジェクトプールに戻して再利用 することで、ゲームのパフォーマンスを向上させます。
① Enemyスクリプトにコードを実装
敵 (Enemy
) が移動ルートの最終地点に到達したときに、イベントを発火してオブジェクトプールに戻す処理 を追加します。
1. ゴール到達時のイベントを定義
// ゴールに到達したときのイベント
public static Action OnReachedGoal;
- 他のスクリプト(例えば
LevelManager
)が、このイベントをリッスンすることで拠点HPを減らす処理を追加できる ようになります。
2. 最終地点に到達したら処理を実行
UpdatePointIndex()
メソッドの中で、最後のポイントなら ReachedGoal()
を呼び出すようにします。
else
{
// 最終地点ならプールに戻す
ReachedGoal();
}
- 敵が最後のポイントに到達した場合、次のポイントに進まずに
ReachedGoal()
を実行 - これにより、最終地点に着いた敵は消える(実際にはプールに戻る)
3. ReachedGoal() メソッド
/// <summary>
/// ゴールに到達したらオブジェクトをプールに戻す
/// </summary>
private void ReachedGoal()
{
// イベントを実行(拠点のHPを減らす処理などに利用)
OnReachedGoal?.Invoke();
// HPをリセット(次のスポーン時に初期状態にする)
enemyHP.ResetHealth();
// 敵オブジェクトをオブジェクトプールに戻す
ObjectPooler.ReturnToPool(gameObject);
}
OnReachedGoal?.Invoke();
で 拠点にダメージを与える処理を別スクリプトで実装可能にするenemyHP.ResetHealth();
で HPを初期値に戻すObjectPooler.ReturnToPool(gameObject);
で 敵を削除せずに非表示にしてプールに戻す
4. 位置リセットのためのメソッド
public void ResetMovePoint()
{
currentMovepointIndex = 0;
}
- 敵が再びスポーンしたとき、最初の移動ポイントからスタートするようにリセット
② EnemyHPスクリプト にコードを追加
敵が HPが0になって倒された場合も、オブジェクトプールに戻す ようにします。
1. 死亡時のイベントを定義
// 敵が死亡したときのイベント
public static Action OnEnemyDead;
- これにより、敵が倒されたときの処理を他のスクリプトで検知できる
2. Die()
メソッドにイベント追加
/// <summary>
/// 敵が倒れたときの処理
/// </summary>
private void Die()
{
// イベントを実行(スコア加算などに利用可能)
OnEnemyDead?.Invoke();
// 敵をオブジェクトプールに戻す
ObjectPooler.ReturnToPool(gameObject);
}
OnEnemyDead?.Invoke();
で スコア加算や他の処理を外部スクリプトで管理可能- 敵を削除するのではなく、オブジェクトプールに戻して再利用する
③ Spawnerスクリプトにコードを追加
現在のコードでは、一体目の敵がゴールに到達すると、残りの敵が最終地点に直線移動してしまうバグ があります。
この問題を修正するために、敵がスポーンするたびに ResetMovePoint()
を実行 し、移動ルートをリセットします。
1. SetEnemy() の修正
//SetEnemyのEnemy enemy = newInstance.GetComponent<Enemy>();enemy.movepoint = movePoint;のしたに書く
{
//リセット
enemy.ResetMovePoint();
✅ これで、敵がゴールに到達しても、次にスポーンした敵が最初のポイントから正しく移動 するようになります。
④ エディタで動作確認
- ゲームを再生(▶ボタンをクリック)
- 敵がゴールに到達したときに消えるか確認
- 敵が再スポーンしたときに、正しく最初の位置から移動するか確認
- 敵がHP0になったとき、正しくプールに戻るか確認
- Spawner のスポーン数を増やし、複数の敵がゴールに到達したときの動作を確認
🎯 まとめ
✅ Enemy
にゴール到達時の処理を追加し、プールに戻すように実装
✅ EnemyHP
に死亡時の処理を追加し、倒された敵もプールに戻す
✅ Spawner
にコードを追加し、ゴール到達後も正しく移動するように修正
✅ エディタでテストし、ゴール到達時の挙動が問題なく動作することを確認

🔥 次は、ウェーブの実装を行い、敵の出現パターンを管理していきます! 🎮
6. ウェーブの実装
タワーディフェンスゲームでは、敵が 「ウェーブ」(一定の間隔で複数回に分けて出現するシステム)でスポーンすることが一般的です。
ここでは、ウェーブの管理を行う LevelManager
を作成し、ウェーブごとに敵をスポーンする仕組み を実装していきます。
LevelManager スクリプト
① Systemフォルダに LevelManager
を作成
- Systemフォルダ に
LevelManager
スクリプトを作成 LevelManager
というオブジェクトを作成し、スクリプトをアタッチ- Transformの値をリセット しておく(0, 0, 0 に設定)
② LevelManager.cs
のコード
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelManager : MonoBehaviour
{
[NonSerialized] public int currentWave;
//シングルトンにする
public static LevelManager instance;
private void Awake()
{
if (instance == null)
{
instance= this;
}
else if (instance != this)
{
Destroy(gameObject);
}
}
// Start is called before the first frame update
void Start()
{
//現在のウェーブを設定する
currentWave= 1;
}
private void WaveCompleted()
{
currentWave++;
}
private void OnEnable()
{
Spawner.OnWaveCompleted += WaveCompleted;
}
private void OnDisable()
{
Spawner.OnWaveCompleted -= WaveCompleted;
}
}
Spawner スクリプト
① Spawner.cs
にコードを追加
//生成できる敵の数
private int enemiesRemaining;
//ウェーブの遅延
[SerializeField] private float waveDelayTime = 1f;
//ウェーブ達成時に呼ばれるアクション
public static Action OnWaveCompleted;
//Start関数に書く
//生成できる数を設定する
enemiesRemaining = enemyCount;
/// <summary>
/// ゴールやデスで消えたエネミーを記録する
/// </summary>
private void RecordEnemy()
{
//このウェーブで生存している敵の数
enemiesRemaining--;
//このウェーブの完了条件を確認する
CurrentWeaveCheck();
}
/// <summary>
/// ウェーブの完了条件を確認して、イベントとコルーチンを呼ぶ
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void CurrentWeaveCheck()
{
if (enemiesRemaining <= 0)
{
//ウェーブ完了時の処理を呼び出す
OnWaveCompleted?.Invoke();
//次のウェーブの準備
StartCoroutine(NextWave());
}
}
/// <summary>
/// 次のウェーブに向けて数値関係をリセット
/// </summary>
/// <returns></returns>
private IEnumerator NextWave()
{
//数値の分待機する
yield return new WaitForSeconds(waveDelayTime);
//生成する敵の数をセット
enemiesRemaining = enemyCount;
//次のスポーンまでの時間
spawnTimer = 0f;
//生成した後
spawned = 0;
}
private void OnEnable()
{
//イベントに関数を登録
Enemy.OnReachedGoal += RecordEnemy;
EnemyHP.OnEnemyDead += RecordEnemy;
}
private void OnDisable()
{
//イベントから関数を消去
Enemy.OnReachedGoal -= RecordEnemy;
EnemyHP.OnEnemyDead -= RecordEnemy;
}
Waypoint_and_Spawnerのinspector画面からWaveDeleyTime次のウェーブまでの時間を設定します。
解説
1. LevelManager.cs
の役割
LevelManager
は、現在のウェーブを管理するシングルトン です。
このクラスの主な機能は以下の通りです。
✅ currentWave
で現在のウェーブ数を管理
✅ ウェーブが完了したら currentWave
を増やす
✅ Spawner.OnWaveCompleted
を監視し、ウェーブの進行を管理する
2. Spawner.cs
の役割
Spawner
は、敵をウェーブ単位でスポーンし、ウェーブ完了を管理 します。
✅ enemiesRemaining
で、現在のウェーブで残っている敵の数を管理
✅ 敵がゴール or 倒されたときに enemiesRemaining
を減らし、すべて消えたら次のウェーブを開始
✅ ウェーブが完了したら OnWaveCompleted
を発火し、LevelManager
に通知
まとめ
✅ LevelManager
を作成し、ウェーブ数を管理
✅ Spawner
にウェーブ管理を追加し、すべての敵が倒れたら次のウェーブを開始
✅ イベントを使って LevelManager
にウェーブ完了を通知
✅ ウェーブ遅延時間 (WaveDelayTime
) を調整可能にする

🔥 次は、拠点の体力を実装し、ゲームオーバー処理を作成していきます! 🎮
7. 拠点の体力管理
タワーディフェンスゲームでは、敵が最終地点(拠点)に到達すると 拠点の体力(HP) が減少し、体力が 0 になると ゲームオーバー になります。このセクションでは、拠点の体力システムを実装し、敵がゴールに到達するたびに体力が減るように設定していきます。
拠点の体力システムの実装
まず、拠点の体力を管理するために LevelManagerスクリプト に変数と処理を追加していきます。
1. 拠点HPの変数を追加
LevelManager.cs
に以下の変数を追加します。
// 実際に値を変更させる体力変数
[NonSerialized] public int currentLife;
// 体力の初期設定だけに使う数値
[SerializeField] private int life = 10;
解説:
currentLife
… 現在の拠点HP。ゲーム中に変更される値life
… 初期設定用のHP(Inspector から変更可能)
2. 体力の初期設定
Start()
関数内で currentLife
に life
の値を代入します。
void Start()
{
// 体力を設定
currentLife = life;
}
3. 敵がゴールしたときの処理を登録
敵が拠点に到達したときに 体力を減らす処理 を実行するため、イベントを登録します。
private void OnEnable()
{
Enemy.OnReachedGoal += ReduceLifes;
}
private void OnDisable()
{
Enemy.OnReachedGoal -= ReduceLifes;
}
解説:
OnEnable()
… ゲームが開始されたときにReduceLifes
を Enemy.OnReachedGoal に登録OnDisable()
… スクリプトが無効化されたときにイベントの登録を解除
4. 体力を減らす処理
敵が拠点に到達すると ReduceLifes()
が呼ばれて体力が減少します。
/// <summary>
/// ライフを減らして体力を確認する
/// </summary>
private void ReduceLifes()
{
// 体力を1減らす
currentLife--;
// 体力が0になったかチェック
if (currentLife <= 0)
{
currentLife = 0;
// ゲームオーバー処理
Debug.Log("ゲームオーバー");
}
}
解説:
currentLife--
で体力を 1 減らす- 体力が 0 以下になったら
Debug.Log("ゲームオーバー");
を表示
動作確認
- Unityエディタで LevelManagerスクリプトをアタッチ したオブジェクトを選択
life
の値を 10 に設定- ゲームを開始し、敵が 拠点に到達 すると
currentLife
が減ることを確認 - 体力が 0 になると Console に “ゲームオーバー” と表示されるかチェック
改善ポイント
Debug.Log("ゲームオーバー")
のみでは見た目の変化がないため、ゲームオーバー時に UI を表示する などの処理を追加すると良い- 体力を UI に表示 することで、プレイヤーが残りのHPを視覚的に確認できるようにする
まとめ
- 拠点の体力(HP)を管理する LevelManagerスクリプトを作成
- 敵が拠点に到達すると体力が減少
- 体力が 0 になるとゲームオーバー
- デバッグログで動作確認
8. まとめ – この記事で学んだことの振り返り
この記事では、Unityで2Dタワーディフェンスゲーム を作る際に欠かせない オブジェクトプール、敵のスポーンシステム、ウェーブ管理、拠点の体力システム の実装方法を解説しました。
パフォーマンスを最適化しながら、より本格的なタワーディフェンスゲームを作るための重要な機能を学びました。
✅ この記事で学んだこと
1. オブジェクトプールの実装
- ObjectPoolerスクリプト を作成し、敵や弾のオブジェクトを効率的に管理
GetObjectFromPool()
を使って 必要なときだけオブジェクトを再利用ReturnToPool()
で 使い終わったオブジェクトを非表示にしてプールへ戻す- 敵のオブジェクトプールと弾のオブジェクトプール を作成し、パフォーマンスを向上
2. 敵のスポーン管理
- Spawnerスクリプト を作成し、一定間隔 / ランダム間隔 で敵をスポーンさせる
- 敵の移動ポイントを設定 し、スポーンした敵が順番に移動するように調整
- スポーンする敵の数を管理 し、上限を超えないよう制御
3. 最終地点に到着した敵の処理
- 敵がゴールに到達したら非表示にする
OnReachedGoal()
を使って 敵がゴールしたことを記録- 敵のHPをリセットし、オブジェクトプールへ戻す ことで次のウェーブでも利用可能に
4. ウェーブ管理の実装
- LevelManagerスクリプトを作成し、ウェーブの進行を管理
OnWaveCompleted
を使って 全ての敵が倒れたら次のウェーブを開始NextWave()
を使い、一定の間隔を置いて次のウェーブが始まるように調整- ウェーブが進むごとに難易度を調整できる 仕組みを作成
5. 拠点の体力システム
- 拠点のHP(ライフ)を管理し、敵がゴールするたびに減少
ReduceLifes()
で 拠点のHPが0になったらゲームオーバーDebug.Log("ゲームオーバー")
を表示して、体力がなくなったことを確認
タワーディフェンスゲームを効率よく作るなら?
本記事では、スポーン管理・ウェーブ管理・オブジェクトプールを自作する方法を解説しましたが、「もっと簡単にタワーディフェンスを作りたい!」 という方には、「Tower Defense Toolkit 4 (TDTK-4)」をおすすめします!
すぐに使えるウェーブシステム・敵AI・タワー配置機能が含まれており、短期間でタワーディフェンスゲームを完成させられます。 自作するかアセットを活用するか、自分に合った方法を選んで開発を進めてみてください!
よくある質問(FAQ)
- Q敵のオブジェクトプールが正しく機能しない
- A
Enemyのプレハブが正しく設定されているか、Waypoint_and_Spawnerの PoolObjectにEnemyプレハブが設定されているか 確認してください。
- QBulletが発射されずに消えてしまう
- A
BulletのSetActive(true)が適切に呼ばれているか、WeaponControlスクリプトで pooler.GetObjectFromPool() が正しく動作しているか 確認してください。
- Q敵がゴールしても次のウェーブが開始されない
- A
Spawnerスクリプトの enemiesRemaining のカウントが正しく減っているか 確認してください。また、Enemy.OnReachedGoalのイベントが正しく登録されているかもチェックしましょう。