Unity標準 ObjectPool の使い方と自作との違い
〜2021以降の開発で「Instantiate地獄」を脱する〜
はじめに
Unity開発でよくある問題のひとつが、次のようなパフォーマンス低下です。
敵・弾・エフェクトなどを Instantiate() / Destroy() で毎回生成していると、GC(ガベージコレクション)によるフレーム落ちが発生する。
こうした「生成・破棄の繰り返し」を防ぐために使われるのが、Object Pool(オブジェクトプール)パターンです。
Unity 2021以降では、UnityEngine.Pool 名前空間に 標準の ObjectPool<T> クラス が追加され、これまで自作していたプール処理を安全かつ簡潔に書けるようになりました。
1. Object Poolとは
Object Poolとは、使い終わったオブジェクトを破棄せずに再利用する仕組みです。

従来の実装(自作Queue方式)
readonly Queue<GameObject> pool = new();
public GameObject Get()
{
if (pool.Count > 0)
{
var obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
return Instantiate(prefab);
}
public void Release(GameObject obj)
{
obj.SetActive(false);
pool.Enqueue(obj);
}
シンプルですが、生成数・破棄タイミング・上限制御などをすべて自分で管理する必要がありました。
2. Unity標準 ObjectPool<T> の登場
Unity 2021.1 以降では、using UnityEngine.Pool; に含まれる ObjectPool<T> が正式実装されました。
これにより、同様の処理を数行で安全に書けます。
基本構文
using UnityEngine;
using UnityEngine.Pool;
public class BulletShooter : MonoBehaviour
{
[SerializeField] GameObject bulletPrefab;
ObjectPool<GameObject> pool;
void Awake()
{
pool = new ObjectPool<GameObject>(
createFunc: () => Instantiate(bulletPrefab),
actionOnGet: b => b.SetActive(true),
actionOnRelease: b => b.SetActive(false),
actionOnDestroy: b => Destroy(b),
collectionCheck: false, // 重複チェック(高速化のためfalse)
defaultCapacity: 10, // 初期確保数
maxSize: 50 // 最大保持数
);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var bullet = pool.Get();
bullet.transform.position = transform.position;
// 一定時間でプールに戻す
StartCoroutine(ReleaseAfterTime(bullet, 1.5f));
}
}
IEnumerator ReleaseAfterTime(GameObject bullet, float time)
{
yield return new WaitForSeconds(time);
pool.Release(bullet);
}
}
動作イメージ

3. 各パラメータの意味
| 引数 | 内容 |
|---|---|
| createFunc | 新しい要素を作成する処理 |
| actionOnGet | プールから取り出したときに実行する処理 |
| actionOnRelease | プールに戻すときの処理 |
| actionOnDestroy | プール容量を超えたときの破棄処理 |
| collectionCheck | 重複登録チェック(デバッグ向け) |
| defaultCapacity | 初期確保数 |
| maxSize | 保持できる最大数 |
4. 自作との違い・メリット比較
| 項目 | 自作プール | Unity標準 ObjectPool<T> |
|---|---|---|
| 実装量 | 多い(Queue管理が必要) | 少ない(数行で完結) |
| 型対応 | GameObject限定が多い | どんな型でも使える(ジェネリック) |
| メモリ管理 | 手動 | 自動(上限超過時に破棄) |
| デバッグ | 独自で実装 | 内部統計・安全チェック付き |
| 速度 | 高速(単純構造) | わずかに低下するが安定性あり |
5. さらに軽量な LinkedPool<T>
LinkedPool<T> は同じ名前空間にある軽量プールです。
内部的に連結リストを使用しており、GC発生をより抑えたい場合に有効です。
using UnityEngine.Pool;
LinkedPool<GameObject> linkedPool = new LinkedPool<GameObject>(
createFunc: () => Instantiate(prefab),
actionOnRelease: b => b.SetActive(false)
);
特徴:
- より低コスト(GC削減)
- Destroy処理やmaxSize制限なし(明示的に管理)
6. 実践的な使い分け
| 用途 | 推奨パターン | 理由 |
|---|---|---|
| 弾丸・敵・エフェクト | ObjectPool<GameObject> | 上限数制御と安全性を両立 |
| 短命UI(ポップアップ等) | LinkedPool<GameObject> | 軽量で高速 |
| システム系(C#オブジェクト) | ObjectPool<T> | 型に依存しない汎用性 |
7. Tips:Unity 6での補足
Unity 6(6000.x系)では、このAPIがさらに安定化しており、collectionCheck 無効時でも安全な動作が保証されています。
ゲームループ内で頻繁に生成されるオブジェクトに対しても、GC発生をほぼゼロに抑えられます。
まとめ
| 比較項目 | 自作プール | Unity標準 ObjectPool<T> |
|---|---|---|
| 実装コスト | 高い | 低い |
| 安定性 | 中 | 高 |
| 柔軟性 | 中 | 高 |
| 対象 | GameObject中心 | 任意の型(構造体もOK) |
おわりに
Unity標準の ObjectPool<T> は、自作よりも安全で柔軟、そして長期的な保守に優れた選択肢です。
特にチーム開発や講義教材では、再利用と拡張性を重視した構成を意識することで、実装の質も学びの深さも大きく向上します。





ディスカッション
コメント一覧
まだ、コメントがありません