[Unity]オブジェクトプーリングをタワーディフェンスゲームに用いる

オブジェクトプーリングとは

ゲームやアプリケーションで頻繁に生成・破棄されるオブジェクトをあらかじめまとめて保管しておき、再利用する手法です。この時の保管場所をプールといいます。プールにはリストやキューといったデータ構造が用いられます。

処理負荷の大きい”オブジェクトの生成・破棄”の回数を減らすことができ、パフォーマンスが向上します。

タワーディフェンスゲームで用いる

タワーディフェンスゲームにおいてプーリングを用いるべきもの

  • 敵ユニット :
    敵ユニットはウェーブごとに多数出現し、ゲームの進行に応じて破壊されるため、オブジェクトプーリングで管理するのが理想的です。
  • プロジェクタイル(弾丸):
    タワーが発射する弾丸は頻繁に生成され、一定時間後に消えるため、これもプールで管理すると効率的です。
  • エフェクト(爆発、ヒットエフェクトなど):
    敵が倒れたときのエフェクトや弾丸が命中したときのエフェクトなども、生成と破棄が多いオブジェクトです。プールで管理すると負荷を軽減できます。

オブジェクトプールの実装例

ObjectPoolクラスの例

以下がObjectPoolクラスのコード例です。

C#
using System.Collections.Generic;
using UnityEngine;

public class ObjectPooler : MonoBehaviour
{
    [System.Serializable]
    public class Pool
    {
        public string tag;           // プールのタグ(オブジェクトを識別するため)
        public GameObject prefab;    // 生成するオブジェクトのプレハブ
        public int size;             // プールに格納するオブジェクトの数
    }

    public List<Pool> pools;        // 複数のプールをリストで管理
    public Dictionary<string, Queue<GameObject>> poolDictionary;  // 各プールをタグごとに辞書で管理

    public static ObjectPooler Instance;  // シングルトンインスタンス(簡単にアクセスするため)

    private void Awake()
    {
        Instance = this;
    }

    void Start()
    {
        // プールディクショナリを初期化
        poolDictionary = new Dictionary<string, Queue<GameObject>>();

        // 各プールにオブジェクトを生成してキューに追加
        foreach (Pool pool in pools)
        {
            Queue<GameObject> objectPool = new Queue<GameObject>();

            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.prefab);  // オブジェクトを生成
                obj.SetActive(false);  // 初期状態では非アクティブ
                objectPool.Enqueue(obj);  // キューに追加
            }

            poolDictionary.Add(pool.tag, objectPool);  // タグとともに辞書に登録
        }
    }

    // プールからオブジェクトを取得して生成する関数
    public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
    {
        // 指定されたタグのプールが存在しない場合の処理
        if (!poolDictionary.ContainsKey(tag))
        {
            Debug.LogWarning("Pool with tag " + tag + " doesn't exist.");
            return null;
        }

        // キューからオブジェクトを取り出す
        GameObject objectToSpawn = poolDictionary[tag].Dequeue();
        objectToSpawn.SetActive(true);  // アクティブにする
        objectToSpawn.transform.position = position;  // 位置を設定
        objectToSpawn.transform.rotation = rotation;  // 回転を設定

        return objectToSpawn;
    }

    // オブジェクトをプールに返す関数
    public void ReturnToPool(GameObject objectToReturn, string tag)
    {
        // 指定されたタグのプールが存在するか確認
        if (!poolDictionary.ContainsKey(tag))
        {
            Debug.LogWarning("Pool with tag " + tag + " doesn't exist.");
            return;
        }

        // オブジェクトを非アクティブにし、キューに戻す
        objectToReturn.SetActive(false);
        poolDictionary[tag].Enqueue(objectToReturn);
    }
}
C#

コード内容の図解

コードの解説

  • [System.Serializable] public class Pool:
    • このクラスは、各プールのデータ(タグ、プレハブ、プールサイズ)を定義します。Serializable でマークされているため、Unity エディタのインスペクターに表示されます。
    • tag: プールの識別子。各オブジェクトに固有のタグを付けて管理します。
    • prefab: プールするオブジェクトのプレハブ。
    • size: プール内に保持するオブジェクトの数。
  • public List<Pool> pools:
    • プールのリストです。複数のプールをこのリストで管理し、それぞれのプールが特定のオブジェクト(prefab)を扱います。
  • public Dictionary<string, Queue<GameObject>> poolDictionary:
    • 各プールをタグで管理するための辞書です。タグをキーにして、各オブジェクトをキュー(Queue)で管理しています。
    • Queue<GameObject> は、FIFO(先入れ先出し)方式でオブジェクトを取り出すためのデータ構造です。
  • public static ObjectPooler Instance:
    • シングルトンパターンで、ObjectPooler クラスのインスタンスにグローバルにアクセスできるようにします。どこからでも簡単に ObjectPooler の機能を使えるようになります。
  • void Start():
    • プールの初期化処理を行います。各プールに対して設定された数だけオブジェクトを生成し、キューに追加します。
  • public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation):
    • 指定されたタグ(tag)に対応するプールからオブジェクトを取得し、位置と回転を設定して返します。取得したオブジェクトはアクティブ状態にします。
  • public void ReturnToPool(GameObject objectToReturn, string tag):
    • オブジェクトをプールに返却します。objectToReturn を非アクティブにしてから、再び対応するプールのキューに追加します。これにより、オブジェクトが再利用可能な状態で保たれます。

EnemySpawnerクラスの例

タワーディフェンスゲームで用いる時にはSpwanerを用いてオブジェクトを召喚します。以下はEnemySpawnerのコード例です。

C#
public class EnemySpawner : MonoBehaviour
{
    public Transform[] spawnPoints;  // 敵のスポーン位置を格納する配列

    void SpawnEnemy()
    {
        // ランダムなスポーン位置を取得
        Vector3 spawnPosition = spawnPoints[Random.Range(0, spawnPoints.Length)].position;

        // オブジェクトプールから敵を生成して配置
        ObjectPooler.Instance.SpawnFromPool("Enemy", spawnPosition, Quaternion.identity);
    }
}
C#

コードの解説

  • spawnPoints: 敵の出現地点の配列。
  • SpawnEnemy(): ランダムに選んだ地点に敵をオブジェクトプールから生成して配置する関数。
  • Random.Range: 配列の中からランダムにスポーン地点を選択。
  • ObjectPooler.Instance.SpawnFromPool: オブジェクトプールから敵を取り出し、指定の位置に生成。

まとめ

今回は2Dタワーディフェンス制作中に知った手法であるオブジェクトプーリングについて、知ったそばからまとめてみました。Unity2021からオブジェクトプールはUnityの標準搭載となったそうなので気になる方は調べてみてください。

参考記事

コメント

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