本文へスキップ
Unity基礎

Unityのオブジェクトプール入門|Cubeを取り出して再利用する

Unity 6のObjectPoolでCubeを再利用する最小例。GetとRelease、使用中3個と待機中2個の上限の違い、UIの配置、返却時の動作を図とコードで学びます。

公開日: 2026-10-24#Unity#CSharp#ObjectPool#Prefab#初心者
ガイドキャラクター
Unity 6のObjectPoolでCubeを再利用する最小例。GetとRelease、使用中3個と待機中2個の上限の違い、UIの配置、返却時の動作を図とコードで学びます。
学習者キャラクター
準備するものと、動作確認で見る結果を先に確認してから進めます。
Unityのオブジェクトプール入門|Cubeを取り出して再利用するの手順と判断条件を示す説明図
仕組みを説明する模式図です。エディタのスクリーンショットではありません。小さい画面では横にスクロールできます。設定値と手順は本文で確認してください。

この記事で作るもの

「取り出す」でCubeを表示し、「全部返す」で非表示にして再利用に備える仕組みを作ります。対象はUnity 6・3D・uGUIです。弾の移動や物理処理を入れる前に、オブジェクトの取り出しと返却だけを確かめます。

オブジェクトプールは、使い終わった物を毎回削除せず、次に使えるよう保管する仕組みです。UnityのObjectPool<T>ではGet()で取り出し、Release()で返します。空のときは新しく作り、待機中の物があればそれを再利用します。ObjectPoolの公式資料

1. PrefabとUIを準備する

  1. シーンにCubeを作り、ProjectウィンドウへドラッグしてPoolCubeというPrefabにします。今回は追加スクリプトやRigidbodyを付けません。
  2. Prefabを作ったら、シーン上の元のCubeは削除します。Project側のPrefabは残します。
  3. 空のGameObjectをPoolDemoと名付け、Positionを(0, 0, 0)にします。
  4. CanvasとEventSystemを用意し、Canvasに有効なGraphic Raycasterがあることを確認します。Buttonを2つ作り、表示文字を「取り出す」「全部返す」にします。ボタンは重ならない位置に置きます。
  5. Main CameraをPosition (0, 1, -10)、Rotation (0, 0, 0)にします。Cubeを照らすライトも残します。
  6. 次のコードを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の配置、返却時の動作を図とコードで学びます。
この記事のまとめを案内するガイドキャラクター

この記事のまとめ

Unity 6のObjectPoolでCubeを再利用する最小例。GetとRelease、使用中3個と待機中2個の上限の違い、UIの配置、返却時の動作を図とコードで学びます。