28
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity公式ObjectPool】プールにオブジェクトを返却する超便利機能!

Last updated at Posted at 2024-02-24

解決したいこと

Unity2021.1から、Unity公式がオブジェクトプールを実装してくれています。

とても便利なのですが、
使い終わったオブジェクトをプールに戻す時、以下のように書く必要があります。

pool.Release(obj);

このメソッドを呼び出すためには、Release呼び出すクラスが

  • プールのインスタンス
  • 返却対象にインスタンス

の両方を参照する必要があります。
そのため、機能が分離できず、返却ロジックも書きにくくて、とても不便です!!

こちらは、Unity標準機能を利用することで、この問題に対処する方法の記事になります!

IDisposableを使う方法

実はPoolからオブジェクトを借りてくるときに、返却機能を一緒に取り出すメソッドが用意されています。

Get()メソッドの返り値を使わずに、Get(out T obj)メソッドを使って、out 修飾子のついた引数でオブジェクトを取り出してください。

仮にBullet型を取り出す場合

- Bullet obj = _pool.Get();

+ IDisposable disposable = _pool.Get(out Bullet obj);

このとき返り値となっている型はIDisposableを実装しており、IDisposable.Dispose()でこのオブジェクトをプールに返却する機能を持っています。

実際にこのとき返り値となっているのはPooledObject<T>という型です。

PooledObjectのコードを見たい方はこちら
  public struct PooledObject<T> : IDisposable where T : class
  {
    private readonly T m_ToReturn;
    private readonly IObjectPool<T> m_Pool;

    internal PooledObject(T value, IObjectPool<T> pool)
    {
      this.m_ToReturn = value;
      this.m_Pool = pool;
    }

    void IDisposable.Dispose() => this.m_Pool.Release(this.m_ToReturn);
  }
  

今回必要なのはIDisposableの機能だけなので、変数宣言でIDisposable型を指定して変換しています。

あとは、このIDisposableをObjの返却処理を担当するクラスに渡してあげれば完成です!

例えばBulletクラスを都度借りてきて返却するコード例は以下のようになります!!

public class Bullet : MonoBehaviour
{
    private IDisposable _disposable;

    public void SetReturner(IDisposable disposable)
    {
        //外部からこのメソッドを叩くことで、返却機能を登録できるようにする
        _disposable = disposable;
    }

    private void Update()
    {
        if ( /*返却の判定処理*/)
        {
            // disposeを呼ぶだけで返却される
            _disposable.Dispose();
        }
    }
}
public class BulletPool : MonoBehaviour
{
    private ObjectPool<Bullet> _bulletPool;

    private void Awake()
    {
       /* _bulletPoolをnewしておく */
    }

    public Bullet GetBullet()
    {
        //out修飾子でbulletを取り出し、返り値をIDisposableとして取得する
        IDisposable disposable = _bulletPool.Get(out var bullet);
        //取得したbulletのインスタンスに、プールへの返却機能を渡す
        bullet.SetReturner(disposable);
        //呼び出し側にBulletのインスタンスを返す
        return bullet;
    }
}

PoolからはIDisposableしか渡さず、
インスタンス側から返却ロジックを叩くことができるようになりました!

生成機能と返却機能を簡単に分離できるようになるので大変便利です!!

おわりに

以上でこの記事はおわりです!

また、今回省略したPoolの生成処理の中では、
オブジェクト返却時のコールバックを登録することができます。

gameObject.SetActive(false)など、
返却時に他の処理が必要な場合はそちらで登録するのをオススメします!

ぜひ素敵なObjectPoolingライフを送ってください!!!

この利用方法は、structであるPooledObjectIDisposableにキャストしているのでボックス化が起ります。
おそらく、本来この機能は、usingを利用して、短い期間で寿命管理ことを想定された機能です。

その機能をハック的に利用しているため、ボックス化が起きてしまいます。
パフォーマンスが重要な場面では、PooledObjectと同じ働きをするclassを自作する方針も検討してみてください。

(検証、ご指摘くださった@junerさま、ありがとうございます!)

※2024/02/25追記>2024/03/04編集

28
16
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?