この記事で作るもの
「取り出す」でCubeを表示し、「全部返す」で非表示にして再利用に備える仕組みを作ります。対象はUnity 6・3D・uGUIです。弾の移動や物理処理を入れる前に、オブジェクトの取り出しと返却だけを確かめます。
オブジェクトプールは、使い終わった物を毎回削除せず、次に使えるよう保管する仕組みです。UnityのObjectPool<T>ではGet()で取り出し、Release()で返します。空のときは新しく作り、待機中の物があればそれを再利用します。ObjectPoolの公式資料
1. PrefabとUIを準備する
- シーンにCubeを作り、Projectウィンドウへドラッグして
PoolCubeというPrefabにします。今回は追加スクリプトやRigidbodyを付けません。 - Prefabを作ったら、シーン上の元のCubeは削除します。Project側のPrefabは残します。
- 空のGameObjectを
PoolDemoと名付け、Positionを(0, 0, 0)にします。 - CanvasとEventSystemを用意し、Canvasに有効なGraphic Raycasterがあることを確認します。Buttonを2つ作り、表示文字を「取り出す」「全部返す」にします。ボタンは重ならない位置に置きます。
- Main CameraをPosition
(0, 1, -10)、Rotation(0, 0, 0)にします。Cubeを照らすライトも残します。 - 次のコードを
CubePoolDemo.csに保存し、PoolDemoへ付けます。InspectorのCube PrefabへProject内のPrefab、Take ButtonとReturn Buttonへ各Buttonを登録します。
ボタンのOn Click()は空にします。クリックの登録はコードで行います。Prefabやボタンの作成が初めてなら、Prefabの基本と最初のUIボタンを先に確認できます。
PoolDemo (CubePoolDemo)
実行時に作られるCubeの親になる
Canvas
TakeButton ← 取り出す
ReturnButton ← 全部返す
EventSystem
Project内: PoolCube.prefab
2. 取り出しと返却を実装する
この例の上限は2種類あります。使用中は最大3個にコード側で制限し、返却後に保管するのは最大2個にします。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;
public sealed class CubePoolDemo : MonoBehaviour
{
[SerializeField] private GameObject cubePrefab;
[SerializeField] private Button takeButton;
[SerializeField] private Button returnButton;
private const int MaxActive = 3;
private readonly List<GameObject> activeCubes = new List<GameObject>();
private ObjectPool<GameObject> pool;
private void OnEnable()
{
if (cubePrefab == null || takeButton == null || returnButton == null)
{
Debug.LogError("Prefabと2つのButtonを登録してください。", this);
enabled = false;
return;
}
if (pool == null)
{
pool = new ObjectPool<GameObject>(
createFunc: CreateCube,
actionOnGet: cube => cube.SetActive(true),
actionOnRelease: cube => cube.SetActive(false),
actionOnDestroy: cube => Destroy(cube),
collectionCheck: true,
defaultCapacity: 2,
maxSize: 2);
}
takeButton.onClick.AddListener(TakeCube);
returnButton.onClick.AddListener(ReturnAll);
LogCounts();
}
private GameObject CreateCube()
{
GameObject cube = Instantiate(cubePrefab, transform);
cube.SetActive(false);
return cube;
}
public void TakeCube()
{
if (!isActiveAndEnabled || pool == null) return;
if (activeCubes.Count >= MaxActive)
{
Debug.Log("使用中は3個までです。先に全部返してください。", this);
return;
}
GameObject cube = pool.Get();
cube.transform.position = transform.position
+ Vector3.right * ((activeCubes.Count - 1) * 2f);
activeCubes.Add(cube);
LogCounts();
}
public void ReturnAll()
{
if (pool == null) return;
foreach (GameObject cube in activeCubes)
{
pool.Release(cube);
}
activeCubes.Clear();
LogCounts();
}
private void LogCounts()
{
Debug.Log($"使用中={pool.CountActive}, 待機中={pool.CountInactive}", this);
}
private void OnDisable()
{
if (takeButton != null) takeButton.onClick.RemoveListener(TakeCube);
if (returnButton != null) returnButton.onClick.RemoveListener(ReturnAll);
ReturnAll();
}
private void OnDestroy()
{
pool?.Dispose();
pool = null;
}
}
取り出すたびに位置を設定するので、再利用したCubeも所定の場所へ戻ります。実際の弾やエフェクトへ広げる場合は、速度・残り時間・見た目など、前回の状態も取り出し時に初期化します。
3. 2種類の上限を区別する
maxSize: 2は、返却された物を保管する上限です。同時に表示できる数を2個へ制限する設定ではありません。 今回は別のMaxActive = 3で使用中の数を制限しています。
defaultCapacity: 2も「開始時にCubeを2個作る」という意味ではありません。内部の保管領域の初期容量です。最初に3個取り出すと、必要に応じて3個作られます。3個を返すと2個が待機し、入りきらない1個は削除されます。コンストラクターの公式資料
動作確認
Consoleを開き、ログの表示を有効にしてPlayします。
| 操作 | 画面とConsoleの期待結果 |
|---|---|
| 開始直後 | Cubeはなく、使用中0・待機中0 |
| 「取り出す」を3回押す | Cubeが横に3個並び、使用中3・待機中0 |
| 4回目を押す | 追加せず、上限のログが出る |
| 「全部返す」を押す | Cubeが消え、使用中0・待機中2 |
| もう一度「全部返す」を押す | 変化なし。二重返却エラーが出ない |
| 再び「取り出す」を2回押す | 待機していたCubeを再利用し、使用中2・待機中0 |
返却済みのCubeはHierarchy上で非アクティブのまま残ります。削除されていないからといって返却失敗ではありません。3個目を返したときの実際の削除はフレーム末尾に反映されます。
うまく動かないとき
- クリックしても変化しない:参照3つ、EventSystem、Graphic Raycasterを確認します。参照未設定で無効になった場合は、修正してコンポーネントを有効にし直します。
- 最初からCubeが1個ある:Prefab作成用の元のCubeをシーンに残していないか確認します。
- 位置がずれる:PoolDemoを原点に置き、PrefabのScaleを
(1, 1, 1)にします。 - MissingReferenceExceptionが出る:使用中・待機中のCubeを別のコードやHierarchyから削除していないか確認します。この教材では、Cubeの削除と非表示はプールの管理役に任せます。
プールは生成・破棄の回数を減らす選択肢です。使えば必ず速くなるとは限らないため、実ゲームではProfilerなどで負荷を比較します。次は性能予算の考え方へ進めます。
この記事のまとめ
Unity 6のObjectPoolでCubeを再利用する最小例。GetとRelease、使用中3個と待機中2個の上限の違い、UIの配置、返却時の動作を図とコードで学びます。