A lightweight and flexible object pooling system for Unity
uPoolsはUnity向けにオブジェクトプール用の機能を提供するライブラリです。汎用的なObjectPoolやGameObjectのプールに特化したGameObjectPoolなどを追加するほか、ObjectPoolBaseクラスを継承して独自のオブジェクトプールを作成することが可能になります。またSharedGameObjectPoolを使用することで手軽にプーリングを行うこともできます。
さらに、UniTaskを用いた非同期オブジェクトプールやAddressablesに対応したオブジェクトプールも用意されています。
- Unity向けのオブジェクトプール用のクラスを多数追加
- 汎用的なオブジェクトプールの機能を提供する
ObjectPool - GameObjectに特化した
GameObjectPool - Instantiate/Destroyをそのまま置き換え可能にする
SharedGameObjectPool IPoolCallbackReceiverを用いてコールバックを取得- UniTaskを用いた非同期オブジェクトプール
- Addressablesに対応した
AddressableGameObjectPool
- Unity 2019.4 以上
- Window > Package ManagerからPackage Managerを開く
- 「+」ボタン > Add package from git URL
- 以下のURLを入力する
https://github.com/AnnulusGames/uPools.git?path=/Assets/uPools
あるいはPackages/manifest.jsonを開き、dependenciesブロックに以下を追記
{
"dependencies": {
"com.annulusgames.u-pools" : "https://github.com/AnnulusGames/uPools.git?path=/Assets/uPools"
}
}Instantiate()をSharedGameObjectPool.Rent()に、Destroy()をSharedGameObjectPool.Return()に置き換えるだけでオブジェクトプーリングを行うことができます。
using UnityEngine;
using uPools;
public class Example : MonoBehaviour
{
[SerializeField] GameObject prefab;
void Start()
{
// 事前にオブジェクトを生成する (不足分は自動で生成される)
SharedGameObjectPool.Prewarm(prefab, 10);
// プールからオブジェクトを取得する
var instance = SharedGameObjectPool.Rent(prefab);
// 使用後はプールにオブジェクトを返す
SharedGameObjectPool.Return(instance);
}
}この方法が最も簡単にプーリングを行えますが、細かく挙動を調整したい場合には以降の方法を使用してください。
通常のclassをプーリングするには、ObjectPool<T>を用いてオブジェクトプールを作成します。
class PooledObject { }
var pool = new ObjectPool<PooledObject>(
createFunc: () => new PooledObject(), // オブジェクトの作成をFunc<T>で渡す
onRent: instance => { }, // Rent時の処理 (オプション)
onReturn: instance => { }, // Return時の処理 (オプション)
onDestroy: instance => { } // プールが破棄された時の処理 (オプション)
)
// 事前に生成を行う
pool.Prewarm(10);
// Rent()でオブジェクトを取得、Return()でオブジェクトをプールに返す
var instance = pool.Rent();
pool.Return(instance);
// プール内のオブジェクトの個数を取得
var count = pool.Count;
// プールの中身を全て消す
pool.Clear();
// Dispose()でプールを破棄する
pool.Dispose();Warning オブジェクトプールはスレッドセーフではない点に注意してください。
GameObjectをプーリングする場合には、専用のGameObjectPoolが用意されています。
// PrefabのGameObject
GameObject original;
var pool = new GameObjectPool(original);
// Rent()でオブジェクトを取得
var instance1 = pool.Rent();
// 取得時に位置や回転、親のTransformを指定できる
Transform parent;
var instance2 = pool.Rent(new Vector3(1f, 2f, 3f), Quaternion.identity, parent);
// Return()でオブジェクトを返却
pool.Return(instance1);
pool.Return(instance2);
// Dispose()でプールを破棄し、GameObjectを全てDestroyする
pool.Dispose();GameObjectはプールから取り出される際にアクティブ化され、返却される際に非アクティブ化されます。
ObjectPoolBase<T>を継承することで独自のオブジェクトプールを作成することができます。
class PooledObject { }
public sealed class CustomObjectPool : ObjectPoolBase<PooledObject>
{
protected override PooledObject CreateInstance()
{
return new PooledObject();
}
protected override void OnDestroy(PooledObject instance)
{
// ClearやDisposeでオブジェクトが破棄された際の処理を記述
}
protected override void OnRent(PooledObject instance)
{
// Rent時の処理を記述
}
protected override void OnReturn(PooledObject instance)
{
// Return時の処理を記述
}
}また、interfaceとしてIObjectPool<T>が提供されており、こちらを実装してオブジェクトプールを作成することも可能です。
IPoolCallbackReceiverを実装することでRent/Return時に処理を挿入することが可能です。
public class CallbackExample : MonoBehaviour, IPoolCallbackReceiver
{
public void OnRent()
{
Debug.Log("rented");
}
public void OnReturn()
{
Debug.Log("returned");
}
}GameObjectPoolまたはSharedGameObjectPoolの場合、対象のオブジェクトおよび子オブジェクトが持つIPoolCallbackReceiverを実装したComponentを取得し、それぞれコールバックの呼び出しを行います。
それ以外のObjectPool<T>やObjectPoolBase<T>を継承したプールなどは、オブジェクトがIPoolCallbackReceiverを実装している場合にコールバックの呼び出しを行います。
IObjectPool<T>を独自に実装したオブジェクトプールの場合、IPoolCallbackReceiverの扱いは実装側の責任になります。必要に応じてこれらのコールバックを呼び出す処理を実装してください。
uPoolsはUniTaskを用いた非同期のオブジェクトプールに対応しています。UniTaskをプロジェクトに追加するとAsyncObjectPool<T>、AsyncObjectPoolBase<T>、IAsyncObjectPool<T>が利用可能になります。
これらのプールはRentやPrewarm、CreateInstanceが非同期となっており、それ以外は通常のObjectPool<T>等と同じです。
Addressablesを使用してGameObjectを生成する場合、読み込んだPrefabのリソースを管理する必要があります。uPoolsはこれに対応したAddressableGameObjectPoolを提供します。基本的な使い方はGameObjectPoolと同じです。
// Prefabのアドレス
var key = "Address";
var pool = new AddressableGameObjectPool(key);
// 使用方法はGameObjectPoolと同じ
var instance1 = pool.Rent();
var instance2 = pool.Rent(new Vector3(1f, 2f, 3f), Quaternion.identity);
pool.Return(instance1);
pool.Return(instance2);
pool.Dispose();また、UniTaskを導入することで非同期版のAsyncAddressableGameObjectPoolを使用することも可能です。
