Unity初心者でもわかるObjectPool(オブジェクトプール)入門チュートリアル
目次
目的
このチュートリアルでは、弾を撃つたびに Instantiate() と Destroy() を呼んでしまうことで起きるフレーム落ちやメモリ負荷を、ObjectPool(オブジェクトプール) を使って解決する方法を学びます。
最終的には次のような仕組みを完成させます。
- スペースキーで弾を発射できる
- 弾は一定時間で消える(実際は再利用)
- 無駄な生成・破棄がなくなる
Step 1. 新しいシーンを作成する
- Unityを開き、新しいシーンを作る(例:PoolSample)
- Main Camera と Directional Light はそのままで構わない
- 空のオブジェクトを作成して「Player」にリネーム
- Playerにスクリプトを追加する
Step 2. まずは普通に弾を撃つ
プールを使わない基本的な仕組みを確認します。
using UnityEngine;
public class SimpleShooter : MonoBehaviour
{
[SerializeField] GameObject bulletPrefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GameObject bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
Destroy(bullet, 1f);
}
}
}
このコードでは、弾を撃つたびに生成と破棄が行われます。
繰り返すとメモリ確保・解放の負荷が増えます。
Step 3. Unity標準のObjectPoolを導入する
3-1. 名前空間を追加
スクリプトの先頭に次を追記します。
using UnityEngine.Pool;
3-2. Playerに新しいスクリプトを作成
次のコードを PooledShooter.cs としてPlayerにアタッチします。
using UnityEngine;
using UnityEngine.Pool;
using System.Collections;
public class PooledShooter : 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,
defaultCapacity: 10,
maxSize: 50
);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var bullet = pool.Get();
bullet.transform.position = transform.position;
StartCoroutine(ReleaseAfterTime(bullet, 1f));
}
}
IEnumerator ReleaseAfterTime(GameObject bullet, float t)
{
yield return new WaitForSeconds(t);
pool.Release(bullet);
}
}
Step 4. 弾のプレハブを用意する
- Hierarchyで「3D Object → Sphere」を作成
- 名前を「Bullet」に変更
- Rigidbodyを追加し、Use Gravityをオフにする
- Scaleを (0.2, 0.2, 0.2) に調整
- ProjectビューにドラッグしてPrefab化
- Hierarchyから削除してよい
Step 5. 実行テスト
- Playerオブジェクトのスクリプト欄にある「Bullet Prefab」に先ほどのBulletを割り当てる
- 再生ボタンを押し、スペースキーを押して弾を発射する
- 弾は1秒後に見えなくなるが、内部ではDestroyされず再利用されている
Step 6. パフォーマンス比較
| 状況 | 実装方法 | 挙動 |
|---|---|---|
| Before | Instantiate/Destroy | 毎回メモリ確保と解放を行うためGCが頻発 |
| After | ObjectPool | 一度作った弾を再利用し、GCの発生を抑制 |
Profilerで実行すると、GC Allocのスパイクが減少していることを確認できます。
Step 7. 応用例
| 内容 | 実装方法 |
|---|---|
| 弾を前方に飛ばす | bullet.GetComponent<Rigidbody>().velocity = transform.forward * 10f; |
| 弾の種類を増やす | プールを種類ごとに分ける |
| 敵やエフェクトにも利用 | 同じ仕組みで再利用可能 |
| より軽量化したい | LinkedPool<T> に変更する |
まとめ
| 比較項目 | Instantiate/Destroy | ObjectPool |
|---|---|---|
| パフォーマンス | 遅くなる | 高速で安定 |
| メモリ効率 | 毎回確保・破棄 | 再利用 |
| 実装量 | 少ない | 少し多いが管理が楽 |
| 向いている用途 | テスト・小規模 | 実際のゲーム開発 |
補足
- ObjectPool<T> は Unity 2021 以降で利用可能
- 名前空間 UnityEngine.Pool を忘れずに追加する
- Get() で取り出し、Release() で戻すのが基本動作
- 弾・敵・エフェクトなどの一時オブジェクトに最適
練習課題
- 弾を前方向に発射するコードを追加する
- 一定距離で戻る処理を実装する
- 敵を一定間隔で出現させ、倒したらプールに戻す
訪問数 3 回, 今日の訪問数 3回





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