Unityゲームの作り方メモタワーディフェンス

【Unity】2Dタワーディフェンスゲームの作り方④!敵のスポーンとオブジェクトプール実装

Unity

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() で倒された敵を非表示にしてプールへ戻す

この方法を使うことで、敵の生成・削除の負荷を大幅に軽減 できます。


③ 敵のプレハブ化

次に、敵オブジェクトをプレハブ化 します。

  1. Hierarchy ウィンドウEnemy オブジェクトを作成
  2. 必要なコンポーネントSpriteRendererRigidbody2D など)を追加
  3. プレハブ化するProject ウィンドウにドラッグ&ドロップ)
  4. Hierarchy にある Enemy を削除(プレハブのみ残す)

④ Waypoint_and_Spawner に ObjectPooler をアタッチ

次に、敵を管理する Waypoint_and_Spawner(元々 MovePoint)オブジェクトに ObjectPoolerスクリプトをアタッチ します。

  1. Waypoint_and_Spawner を選択
  2. Inspector の「Add Component」 から ObjectPooler を追加
  3. 「PoolObject」欄に、先ほど作成した Enemy のプレハブを設定

💡 これで Waypoint_and_Spawner敵の生成・管理を担当するオブジェクト になりました!


⑤ エディタでEnemyが生成されているか確認

  1. ゲームを再生(▶ボタンをクリック)
  2. HierarchyPool - Enemy というオブジェクトが生成されることを確認
  3. Pool - Enemy の中に 非表示の敵オブジェクトが 10 体入っていることをチェック

もし敵が生成されない場合は、以下の点を確認してください。

ObjectPooler スクリプトが Waypoint_and_Spawner にアタッチされているか
PoolObjectEnemy プレハブが正しく設定されているか
poolSize が 0 になっていないか

これで、敵のオブジェクトプール を作成し、必要に応じて敵を生成・再利用する仕組み が完成しました!
次は、武器と弾のオブジェクトプール を作成し、弾の発射処理を最適化 していきます。 🎯🚀




3. 武器のオブジェクトプールの作成

前のセクションで、敵のオブジェクトプール を作成し、パフォーマンスの最適化を行いました。
次に、弾(Bullet)もオブジェクトプールで管理し、発射&回収をスムーズにする仕組み を作ります。


① FireWeapon に ObjectPooler をアタッチ

まず、武器 (FireWeapon) に ObjectPooler スクリプトをアタッチ し、弾をオブジェクトプールで管理 できるようにします。

  1. Hierarchy ウィンドウFireWeapon オブジェクトを選択
  2. Inspector の「Add Component」 から ObjectPooler を追加
  3. 「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の発射&回収をエディタで確認

  1. ゲームを再生(▶ボタンをクリック)
  2. FireWeapon から 弾が発射されるか確認
  3. 敵に当たると、弾が プールに戻って非表示になるか確認
  4. しばらくすると、再び 弾が発射されるか確認

🔹 チェックポイントBullet がオブジェクトプールから取得されているか?
✅ 敵に当たると BulletDestroy されず、非表示になっているか?
次の弾が発射されたとき、前の弾が再利用されているか?

💡 もし弾が発射されない場合は、以下を確認してください。

  • FireWeaponObjectPooler が正しくアタッチされているか?
  • PoolObjectBulletプレハブ が設定されているか?
  • CheckDistance()ObjectPooler.ReturnToPool(gameObject); が正しく呼ばれているか?

まとめ

FireWeaponObjectPooler を追加し、弾を管理するように設定
WeaponControl を修正し、弾をオブジェクトプールから取得
Bullet を修正し、消えたらオブジェクトプールに戻す
エディタで弾の発射&回収を確認

これで、弾の無駄な生成・削除をなくし、ゲームのパフォーマンスを向上 させることができました!🎯🚀次は、敵のスポーンシステムの実装 に進みます!




4. 敵のスポーン管理

タワーディフェンスゲームでは、一定間隔またはランダムなタイミングで敵を出現させる 必要があります。
ここでは、敵をオブジェクトプールから取得し、スポーンさせるシステム を実装していきます!


① Spawnerスクリプトを作成し、Waypoint_and_Spawnerにアタッチ

まず、敵をスポーンさせる管理スクリプト Spawner を作成し、Waypoint_and_Spawner にアタッチします。

  1. System フォルダ内に 「Spawner」スクリプト を作成
  2. 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敵のスポーンを管理するオブジェクト になりました!

② エディタで敵が一定間隔でスポーンするか確認

  1. ゲームを再生(▶ボタンをクリック)
  2. Waypoint_and_Spawner から敵が 設定した間隔でスポーンするか確認
  3. 敵の数が enemyCount に達すると、それ以上スポーンしないことを確認
  4. スポーンモード(一定間隔 / ランダム)を変更し、動作をチェック

🔹 チェックポイント敵が一定間隔またはランダムな間隔で出現するか?
スポーン数が enemyCount を超えないか?
敵が Spawner の位置からスタートして適切に移動するか?

💡 もし敵が出現しない場合は、以下を確認してください。

  • SpawnerObjectPooler が正しくアタッチされているか?
  • PoolObjectEnemy プレハブが設定されているか?
  • 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();

✅ これで、敵がゴールに到達しても、次にスポーンした敵が最初のポイントから正しく移動 するようになります。


④ エディタで動作確認

  1. ゲームを再生(▶ボタンをクリック)
  2. 敵がゴールに到達したときに消えるか確認
  3. 敵が再スポーンしたときに、正しく最初の位置から移動するか確認
  4. 敵がHP0になったとき、正しくプールに戻るか確認
  5. Spawner のスポーン数を増やし、複数の敵がゴールに到達したときの動作を確認

🎯 まとめ

Enemy にゴール到達時の処理を追加し、プールに戻すように実装
EnemyHP に死亡時の処理を追加し、倒された敵もプールに戻す
Spawner にコードを追加し、ゴール到達後も正しく移動するように修正
エディタでテストし、ゴール到達時の挙動が問題なく動作することを確認

🔥 次は、ウェーブの実装を行い、敵の出現パターンを管理していきます! 🎮




6. ウェーブの実装

タワーディフェンスゲームでは、敵が 「ウェーブ」(一定の間隔で複数回に分けて出現するシステム)でスポーンすることが一般的です。
ここでは、ウェーブの管理を行う LevelManager を作成し、ウェーブごとに敵をスポーンする仕組み を実装していきます。


LevelManager スクリプト

① Systemフォルダに LevelManager を作成

  1. SystemフォルダLevelManager スクリプトを作成
  2. LevelManager というオブジェクトを作成し、スクリプトをアタッチ
  3. 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() 関数内で currentLifelife の値を代入します。

void Start()
{
// 体力を設定
currentLife = life;
}

3. 敵がゴールしたときの処理を登録

敵が拠点に到達したときに 体力を減らす処理 を実行するため、イベントを登録します。

private void OnEnable()
{
Enemy.OnReachedGoal += ReduceLifes;
}

private void OnDisable()
{
Enemy.OnReachedGoal -= ReduceLifes;
}

解説:

  • OnEnable() … ゲームが開始されたときに ReduceLifesEnemy.OnReachedGoal に登録
  • OnDisable() … スクリプトが無効化されたときにイベントの登録を解除

4. 体力を減らす処理

敵が拠点に到達すると ReduceLifes() が呼ばれて体力が減少します。

/// <summary>
/// ライフを減らして体力を確認する
/// </summary>
private void ReduceLifes()
{
// 体力を1減らす
currentLife--;

// 体力が0になったかチェック
if (currentLife <= 0)
{
currentLife = 0;
// ゲームオーバー処理
Debug.Log("ゲームオーバー");
}
}

解説:

  • currentLife-- で体力を 1 減らす
  • 体力が 0 以下になったら Debug.Log("ゲームオーバー"); を表示

動作確認

  1. Unityエディタで LevelManagerスクリプトをアタッチ したオブジェクトを選択
  2. life の値を 10 に設定
  3. ゲームを開始し、敵が 拠点に到達 すると currentLife が減ることを確認
  4. 体力が 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プレハブが設定されているか 確認してください。

Q
Bulletが発射されずに消えてしまう
A

BulletのSetActive(true)が適切に呼ばれているか、WeaponControlスクリプトで pooler.GetObjectFromPool() が正しく動作しているか 確認してください。

Q
敵がゴールしても次のウェーブが開始されない
A

Spawnerスクリプトの enemiesRemaining のカウントが正しく減っているか 確認してください。また、Enemy.OnReachedGoalのイベントが正しく登録されているかもチェックしましょう。

タイトルとURLをコピーしました