🚀

Unityで簡単☆オブジェクトプール

2022/10/18に公開

Qiitaより転載
2017/1/16 初投稿


2017/2/5 追記:デキューするときにデキューしたオブジェクトを返すようにしました。

Unityでプレハブ使うとき、Instantiate使いたくねーなー、でもプール作るのめんどくさいなー、って思っとことありますよね? あるんですよ。そんなあなたに、簡単オブジェクトプールをご紹介。

プールするオブジェクト

まずプールするためのルートオブジェクトを作ります。プールするオブジェクトはこれを継承することで作ります。

PoolableObject.cs
using UnityEngine;

// abstractである必要はないけど、僕はこれを単体で使うことがないので
public abstract class PoolableObject : MonoBehaviour
{
    // 使い終わったら戻すためにプールへの参照を持ちます。
    public GenericPool Pool { private get; set; }
    // Start()が呼べないのでInitを別途実装します。
    public abstract void Init();

    /// <summary>
    /// まあ、忘れそうなので。
    /// プールに戻します。(実態はReturnToPool())
    /// </summary>
    /// <param name="obj"></param>
    protected new void Destroy(Object obj)
    {
        ReturnToPool();
    }

    /// <summary>
    /// プールに戻します。
    /// </summary>
    protected void ReturnToPool()
    {
        Pool.Return(this);
    }
}

プール本体

で、上を実際に作るプールはこちら。

GenericPool.cs
using System.Collections.Generic;
using UnityEngine;

// 名前がGenericなのはジェネリクスを使おうとして失敗した名残。。
public class GenericPool : MonoBehaviour
{
    // ここにプールしたいPrefabをエディタで指定します
    [SerializeField]
    private PoolableObject PooledObject;

    // プールのサイズを指定しておきます(メモリ節約)
    // 必要に応じて調整してください。
    private const int PoolSize = 50;
    // プールの実態。Queueを使用します。
    private readonly Queue<PoolableObject> _pool =
                                       new Queue<PoolableObject>(PoolSize);
    // どうでもいい定数。あんまり回転いじらないのでゼロを作ってるだけ。
    private static readonly Quaternion NoRotation = Quaternion.Euler(0, 0, 0);

    /// <summary>
    /// プールにオブジェクトがあればそれを利用します。
    /// 無ければ新たにオブジェクトをInstantiateします。
    /// </summary>
    /// <param name="position"></param>
    public T Place<T>(Vector2 position) where T : PoolableObject
    {
        return (T) Place(position);
    }

    /// <summary>
    /// プールにオブジェクトがあればそれを利用します。
    /// 無ければ新たにオブジェクトをInstantiateします。
    /// </summary>
    /// <param name="position"></param>
    public PoolableObject Place(Vector2 position)
    {
        PoolableObject obj;
        if (_pool.Count > 0)
        {
            obj = _pool.Dequeue();
            obj.gameObject.SetActive(true);
            obj.transform.position = position;
            obj.Init();
        }
        else
        {
            obj = Instantiate(PooledObject, position, PooledObject.transform.rotation);
            obj.Pool = this;
            obj.Init();
        }
        return obj;
    }

    /// <summary>
    /// オブジェクトをプールに戻します
    /// </summary>
    /// <param name="obj"></param>
    public void Return(PoolableObject obj)
    {
        obj.gameObject.SetActive(false);
        _pool.Enqueue(obj);
    }

}

使い方

使い方はいたってシンプル。

  1. プールしたいプレハブをPoolableObject継承にする。
  2. エディタ上で空のGameObjectを作って、それにGenericPoolをAdd Componentする。
  3. GenericPoolのPooledObjectに1.で作ったプレハブをセットする。
  4. GenericPoolのPlace()を呼ぶ。

以上です。

PoolableObjectを継承した例はこんな感じ。シューティングで使う弾。

Bullet.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D), typeof(Collider2D))]
public class Bullet : PoolableObject
{
    [SerializeField]
    private Rigidbody2D Rigidbody;   
    [SerializeField]
    protected Vector2 Velocity;
    
    public override void Init()
    {
        // 移動速度を設定する
        Rigidbody.velocity = Velocity;
    }

    void Update ()
    {
        // 上に向かって進むので、y > 5になったらプールに戻す。
        if (transform.position.y > 5f)
        {
            ReturnToPool();
        }
    }

    void OnTriggerEnter2D(Collider2D col)
    {
        // 何かにぶつかったらプールに戻す
        ReturnToPool();
    }
}

Discussion