Menu
CoddyTech

UnityのObjectPool

CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 48/64。

Unity にはプール ObjectPool<T> が UnityEngine.Pool に用意されています。4つのラムダ式で各段階の動作を指定すると、プールが予備のオブジェクトと数を管理します。この煙突は0.2秒ごとに煙を吐き出し、それぞれの煙は1秒後にプールへ戻ります。

using UnityEngine;
using UnityEngine.Pool;

public class Chimney : MonoBehaviour
{
    public Puff puffPrefab;
    private ObjectPool<Puff> pool;
    private float timer;

    void Awake()
    {
        pool = new ObjectPool<Puff>(
            createFunc: () => Instantiate(puffPrefab),
            actionOnGet: puff => puff.gameObject.SetActive(true),
            actionOnRelease: puff => puff.gameObject.SetActive(false),
            actionOnDestroy: puff => Destroy(puff.gameObject),
            collectionCheck: true,
            defaultCapacity: 10,
            maxSize: 20);
    }

    void Update()
    {
        timer += Time.deltaTime;
        if (timer >= 0.2f)
        {
            timer = 0f;
            Puff puff = pool.Get();
            puff.transform.position = transform.position + Vector3.up;
            puff.Launch(pool);
        }
    }
}
// Puff.cs
private IObjectPool<Puff> pool;
private float age;

public void Launch(IObjectPool<Puff> owner)
{
    pool = owner;
    age = 0f;
}

void Update()
{
    transform.Translate(0f, Time.deltaTime, 0f);
    age += Time.deltaTime;
    if (age >= 1f) pool.Release(this);
}

createFuncはGetでプールが空だったときに実行され、actionOnGetはGetのたびに、actionOnReleaseはReleaseのたびに実行されます。maxSize個の予備がすでに待機している状態で煙がプールに戻されると、プールはその煙を保持せず、actionOnDestroyで破棄します。defaultCapacityはプールのリストの初期サイズにすぎないため、最初にオブジェクトが作られることはありません。CountAllはプールが作成し、現在も所有している煙の数、CountActiveはゲーム内に出ている煙の数、CountInactiveは予備の数を数えます。Getは煙突が配置する前に煙を有効化しますが、この場合は問題ありません。Puffには位置を読み取るOnEnableがないからです。煙はプールを、すべてのUnityプールが実装しているインターフェースであるIObjectPool<Puff>として保持し、Launchは使用するたびに経過時間をリセットします。

collectionCheckを有効にすると、すでにプールにある煙を戻したときにInvalidOperationExceptionがスローされます。Trying to release an object that has already been released to the pool. このチェックはEditorでのみ実行されます。Releaseのたびに予備を検索するためです。ビルドでは煙がプールに2つある状態になり、2回のGetで同じオブジェクトが渡されてしまいます。よくある原因は、解放後も動き続けてもう一度自身を解放するオブジェクトです。そのため、actionOnReleaseでオブジェクトを無効にしています。

challenge icon

チャレンジ

中級

Unityのプールから花火を打ち上げましょう。

  1. LauncherのAwakeでObjectPool<Spark>を作成します。Instantiate(sparkPrefab)で生成し、取り出したときに有効化し、返却時に無効化し、解放時にオブジェクトを破棄します。maxSize: 5に設定します。
  2. Updateでは、Instantiateの代わりにpool.Get()で各Sparkを取り出します。
  3. Returnでは、Destroyの代わりにpool.Release(spark)を呼び出します。

「プレイ」を押して、何度か花火を打ち上げます。その後、「確認」を押します。

自分で試してみよう

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;

public class Launcher : MonoBehaviour
{
    public Spark sparkPrefab;
    public int sparks = 8;

    void Awake()
    {
        // プールを作成する:生成、取得、返却、破棄を設定し、予備のスパークは最大 5 個にする
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            for (int i = 0; i < sparks; i++)
            {
                Spark spark = Instantiate(sparkPrefab);
                spark.transform.position = transform.position + Vector3.up;
                spark.Launch(Direction(i), this);
            }
        }
    }

    // 寿命を迎えたスパーク
    public void Return(Spark spark)
    {
        Destroy(spark.gameObject);
    }

    // ランチャーの周囲に均等に広がるよう、外側かつ上向きに
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}

Unityのゲームアーキテクチャのすべてのレッスン

自分で練習してみよう: Unityプレイグラウンド