へっぽこ日記

Unity向に記事を投稿しています。たまにUIの事とかも呟くかも。

ObjectPoolを試してみる

はじめに

今回はObjectPoolを試してみる記事です。 ObjectPool自体は知っていたのですが、使ったことがないので試してみました。

サンプルプロジェクト github.com 使用アセット assetstore.unity.com

そもそもObjectPoolとは

繰り返し使うオブジェクトを、愚直に生成と破壊のサイクルを短期間で行うと激重です。そのため、生成したものを再利用することで生成する回数を抑えつつ、破壊する代わりに非表示にすることで負荷を抑えようとするデザインパターンです。

今回は、2021fからUnity公式から導入されているUnityObjectPoolで試してみます。

ObjectPoolを使うために必要な設定

Unityが提供しているObjectPoolは、最低でも以下4つのパラメータをコンストラクタに設定しないといけません。

new ObjectPool<T>(
createFunc: Create, //オブジェクトの生成処理
actionOnGet: OnGetFromPool, // オブジェクトをプールから取り出した時の処理
actionOnRelease: OnReleaseToPool, // オブジェクトをプールに戻したときの処理
actionOnDestroy: OnDestroyPooledObject, //未使用のオブジェクトが一定量を超えた時の処理
)
1.createFunc

オブジェクトの生成処理を担当する関数です。単純にInstantiate()したものを返します。一番最初と非アクティブのオブジェクトが無い時に使われます。

 private ParticleSystem Create()
    {
        return Instantiate(_effect, new Vector3(Random.Range(-10, 10), Random.Range(-10, 10), Random.Range(-10, 10)),
            Quaternion.identity);
    }
2.actionOnGet

プールからオブジェクトを貰ってくるときに実行する関数です。ここではオブジェクトを表示にしています。

private void OnGetFromPool(ParticleSystem effect)
    {
        effect.gameObject.SetActive(true);
    }
3.actionOnRelease

プールへオブジェクトを返却する時に実行する関数です。ここではオブジェクトを非表示にしています。

    private void OnReleaseToPool(ParticleSystem effect)
    {
        effect.gameObject.SetActive(false);
    }
4.actionOnDestroy

非アクティブが多くなりすぎた時にオブジェクトを削除する関数です。単純にDestroy()で削除しています。

private void OnDestroyPooledObject(ParticleSystem effect)
    {
        Destroy(effect);
    }
5.その他

必須ではないのですが、設定することができる値があります。好みに合わせて設定してください。

_pool = new ObjectPool<ParticleSystem>(
            collectionCheck: true, // 同一インスタンスが登録されていないかチェックするかどうか
            maxSize: 100 //オブジェクトの最大許容量
        );

ObjectPoolを利用して作ったもの

今回は単純にパーティクルをObjectPoolで使いまわすものになってます。

  • ParticlePool.cs : ObjectPoolの役割をしています
[SerializeField]
    private ParticleSystem _effect = null;
    
    private ObjectPool<ParticleSystem> _pool;

    private void Awake()
    {
        _pool = new ObjectPool<ParticleSystem>(
            createFunc: Create,
            actionOnGet: OnGetFromPool, // プールから取り出されたときの処理 
            actionOnRelease: OnReleaseToPool, // プールに戻したときの処理
            actionOnDestroy: OnDestroyPooledObject, // プールがmaxSizeを超えたときの処理
            collectionCheck: true, // 同一インスタンスが登録されていないかチェックするかどうか
            maxSize: 100 //オブジェクトの最大許容量
        );
    }
    
    /// <summary>
    /// オブジェクトを生成する
    /// </summary>
    /// <returns></returns>
    private ParticleSystem Create()
    {
        return Instantiate(_effect, new Vector3(Random.Range(-10, 10), Random.Range(-10, 10), Random.Range(-10, 10)),
            Quaternion.identity);
    }
    
    /// <summary>
    /// 非アクティブのオブジェクトが合ったら表示する
    /// </summary>
    /// <param name="effect"></param>
    private void OnGetFromPool(ParticleSystem effect)
    {
        effect.gameObject.SetActive(true);
    }
    
    /// <summary>
    /// プールに返却時にオブジェクトを非表示にする
    /// </summary>
    /// <param name="effect"></param>
    private void OnReleaseToPool(ParticleSystem effect)
    {
        effect.gameObject.SetActive(false);
    }
    
    /// <summary>
    /// 非アクティブが多くなりすぎた時にオブジェクトを削除する
    /// </summary>
    /// <param name="effect"></param>
    private void OnDestroyPooledObject(ParticleSystem effect)
    {
        Destroy(effect);
    }
    
    /// <summary>
    ///プールからオブジェクトを持ってくる 
    /// </summary>
    public void Explosion()
    { 
        _pool.Get();
    }
    
    /// <summary>
    /// オブジェクトを返却する
    /// </summary>
    /// <param name="effect"></param>
    public void Delete(ParticleSystem effect)
    {
        _pool.Release(effect);
    }
  • Particle.cs : パーティクル再生終了時にオブジェクトを返却するようにしています
 private ParticlePool _pool;

    private void Start()
    {
        _pool = GameObject.Find("ObjectPoolManager").gameObject.GetComponent<ParticlePool>();
    }

    //パーティクルの再生が終わった時に実行
    private void OnParticleSystemStopped()
    {
        _pool.Delete(this.GetComponent<ParticleSystem>());
    }
  • Tester.cs : ここでObjectPoolを操作しています
[SerializeField] private ParticlePool _manager;
   
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            _manager.Explosion();
        }
    }

おわりに

簡単に試すことが出来ました。自前でObjectPoolを実装するのは手間が掛かるので、公式から提供されているのは助かりますね。今回はUnityに導入されている物を試しましたが、UniRxにもObjectPoolがあるので、今後はUniRxのObjectPoolも試してみたいと思います。

是非、読者登録をしていただくと助かります!