top of page

Unityの処理落ちを防げ!「オブジェクトプール」の基本と実装ガイド

「オブジェクトプール」の基本と実装ガイド

ゲーム開発が進んでくると、避けて通れないのが「動作のカクつき(処理落ち)」の問題です。

特に弾幕シューティングや、大量の敵が出現するアクションゲームを作っていると、Instantiate() と Destroy() を多用しがちですが、実はこれがパフォーマンス低下の大きな原因になります。

今回は、初心者の方でも導入できる「オブジェクトプール」の仕組みと簡単なコード例をご紹介します。


【目次】


1. なぜ Instantiate() と Destroy() は重いのか?

通常、オブジェクトを生成・破棄するときは以下のメソッドを使います。

  • Instantiate(): メモリを確保し、新しいオブジェクトを生成する。

  • Destroy(): オブジェクトを破棄し、メモリを解放する。

一見、何の問題もないように見えますが、これらを1フレーム内に数十、数百回と繰り返すと、「ガベージコレクション(GC)」というメモリの掃除屋さんが動き出します。この掃除が実行される瞬間に、ゲームが一瞬止まる(カクつく)現象が発生するのです。

2. 「オブジェクトプール」の考え方

オブジェクトプールは、例えるなら「使い捨ての紙コップをやめて、洗って使い回すプラスチックのコップにする」ような仕組みです。

  1. 最初に必要な分だけオブジェクトを作っておく(非アクティブ状態で保持)。

  2. 必要になったら、プールから取り出して「アクティブ」にする。

  3. 使い終わったら、消さずに「非アクティブ」にしてプールに戻す。

これにより、メモリの確保・解放という重い処理をゼロにできます。


3. 【実践】オブジェクトプールのサンプルコード

Unity 2021以降では標準の UnityEngine.Pool が用意されていますが、今回は仕組みを理解するために、最もシンプルで汎用的な自作スクリプトの例を紹介します。

ObjectPool.cs

まずは、オブジェクトを管理するプール側のコードです。


using System.Collections.Generic;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
    public static ObjectPool Instance; // どこからでも呼べるようにシングルトン化

    [SerializeField] private GameObject prefab; // プールしたいプレハブ
    [SerializeField] private int maxPoolSize = 20; // 最初に用意する数

    private List<GameObject> pool = new List<GameObject>();

    private void Awake()
    {
        Instance = this;

        // 最初にオブジェクトを生成して非アクティブでリストに入れておく
        for (int i = 0; i < maxPoolSize; i++)
        {
            GameObject obj = Instantiate(prefab);
            obj.SetActive(false);
            pool.Add(obj);
        }
    }

    // プールからオブジェクトを借りる
    public GameObject GetObject()
    {
        foreach (GameObject obj in pool)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }
        return null; // 足りなくなった場合はnull(拡張する設計もアリ)
    }
}

使い方:弾を発射する例

Instantiate の代わりに GetObject を使うだけです。

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        // ObjectPoolから借りてくる
        GameObject bullet = ObjectPool.Instance.GetObject();
        
        if (bullet != null)
        {
            bullet.transform.position = transform.position;
        }
    }
}

※弾側のスクリプトでは、一定時間経ったら Destroy(gameObject) するのではなく、 gameObject.SetActive(false) とすることでプールに戻すことができます。


4. まとめ:将来の自分のために

最初は Instantiate でも問題なく動くかもしれません。しかし、ゲームの規模が大きくなった時に修正するのは大変です。

  • 大量に生成・破棄するもの(弾、エフェクト、敵)

  • 頻繁に使うもの

これらに関しては、最初からオブジェクトプールを意識して実装しておくことを強くおすすめします。

快適でスムーズなゲーム体験を目指して、ぜひ活用してみてください!

最新記事

すべて表示
ゲーム利用ガイドライン

✅ 実況・配信について YouTube / Twitch / ニコニコ動画 / X(旧Twitter) などでの 実況・プレイ動画の投稿やライブ配信は自由に行っていただけます。 動画・投稿には 公式サイトURLの記載をお願いします。 → https://www.amagirasuquest.com/ 例)「このゲームはこちらから → https://www.amagirasuquest.com/

 
 
個人開発ゲーム制作者の「あるある」20連発

こんにちは、アマギラスです。個人でゲーム開発をしているとふとしたタイミングでアイデアが思い浮かんだり、途端にやる気がなくなったり、嫌気がさしたり…色々ありますよね? そんなあるあるを20個ほどご用意しました。個人開発ゲーム制作者の「あるある」20連発少しでも共感していただけ...

 
 
bottom of page