AIゲーム制作ラボ
Unity×AI6分で読めます

毎フレーム作って壊すのをやめる(オブジェクトプールの効き目を実測)

弾と敵が同時に数百個出るゲームを作りました。表示のたびに GameObject を作って壊す書き方と、作ったものを使い回す書き方を、同じ場面で測って比べます。1フレーム 19.06ms0.15ms になり、確保するメモリは 0 バイトになりました。

Libra

公開 2026年9月19日

この記事の要点4 points

  1. 同じ場面(画面に361個)で測ると、19.06ms0.15ms 60fps の持ち時間 16.7ms を、作って壊す書き方だけで使い切る。
  2. 確保するメモリは1,904,640バイト0バイト(60フレームぶん)。 掃除が走る=カクつきの元。
  3. 使い回すのは絵(GameObject)だけではない。 中身の配列も作り直さない
  4. 🔴 ただしふだんの場面では差が出ない 数十個なら、どちらでも 60fps だった。
目次6
検証した環境最終確認 2026年9月19日
  • Windows 11 / RTX 2080 Ti
  • Unity 6.6(6000.6.0f1)・WebGL ビルド
  • Claude Code(Opus 5 / 思考レベル 高)

書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。

弾と敵が同時に数百個出るゲームを作りました。ここで初めて「毎フレーム作って壊す」が問題になります。

同じゲーム・同じ場面で、2つの書き方を測りました。

19.06ms

作って壊す

1フレーム(361個)

0.15ms

使い回す

同じ場面

1.9MB

作って壊すと確保

60フレームぶん

0バイト

使い回すと確保

同じ60フレーム

何を比べたか

比べたのは描画だけです。ゲームの中身(弾や敵の位置を進める処理)は同じで、画面に出す部分だけを2通り書きました。

毎フレーム作って壊す

  • フレームの最初に、前のフレームのものを全部 Destroy
  • いま必要な数だけ GameObject を作る
  • 書くのは簡単。数が少ないうちは何も起きない

作ったものを使い回す(プール)

  • 作ったものは持っておく。足りないときだけ作る
  • 余ったぶんは消さずに隠す
  • 位置と大きさを入れ直すだけ

使い回すほうの中身は、これだけです。

public SpriteRenderer Next(float x, float y, float diameter)
{
    SpriteRenderer sr = _used < _items.Count ? _items[_used] : Create();  // 足りないときだけ作る
    _used++;
    sr.transform.localPosition = new Vector3(x, y, 0f);
    sr.transform.localScale = new Vector3(diameter, diameter, 1f);
    if (!sr.gameObject.activeSelf) sr.gameObject.SetActive(true);
    return sr;
}

public void End()
{
    for (int i = _used; i < _items.Count; i++)     // 使わなかったぶんは「隠す」だけ
    {
        if (_items[i].gameObject.activeSelf) _items[i].gameObject.SetActive(false);
    }
}

測った結果

画面に敵60・自機の弾300・自機1=361個が出ている状態で、描画を60回くり返して測りました。

Unity のコンソール(エディターの再生中)OK
画面にあるもの:敵60 自機の弾300 合計361個
プールあり: 60回 9.0ms(1回 0.15ms)確保 0バイト
プールなし: 60回 1143.8ms(1回 19.06ms)確保 1904640バイト
プールあり: 60回 8.8ms(1回 0.15ms)確保 0バイト

60fps を保つには、1フレームを 16.7ms に収める必要があります。作って壊す書き方は、描画だけでそれを超えました。しかもフレームごとに約31KB を確保するので、そのうち掃除(GC)が走ります。

同じ絵を出すのに、127倍の時間がかかっていた。

中身(ロジック)側も同じ

使い回すのは絵だけではありません。弾や敵の入れものも、毎フレーム作り直さないようにしています。

このゲームでは、弾・敵を最初に確保した配列に入れ、生きているものだけを前に詰めています。消すときは、最後のものを穴に移して数を1減らすだけです。

// 消し方:並べ替えない・new しない
void RemoveShot(int i) => _shots[i] = _shots[--_shotCount];
void RemoveEnemy(int i) => _enemies[i] = _enemies[--_enemyCount];

これで「消えた印を見に行く」無駄も、詰め直しの並べ替えも要らなくなります。効いているかどうかは、テストで見張っています。

[Test]
public void 進めるあいだ新しくメモリを取らない()
{
    ...
    long before = GC.GetTotalMemory(true);
    for (int i = 0; i < 6000; i++) battle.Tick(Frame, input);
    long after = GC.GetTotalMemory(true);

    Assert.That(after - before, Is.LessThanOrEqualTo(0L), $"6000フレームで {after - before} バイト増えた");
}

テストにすると、あとから戻らない

速さの工夫は、あとから誰か(AI も)が普通の書き方に戻してしまうのが怖いところです。「確保が増えたら落ちるテスト」にしておくと、戻った瞬間に分かります。

ふだんの場面では差が出なかった

正直に書くと、遊んでいる最中の画面では差が出ませんでした。実際に出ているのは数十個で、ブラウザで測るとどちらも 60fps・最長フレーム 16.9ms

Git Bash(本番のページをスマホの見た目で開いて10秒数える)OK
{"url":"https://aigame.game-union-libra.com/play/mureuchi/","fps":60.1,"worstMs":16.9,"frames":601}
{"url":"https://aigame.game-union-libra.com/play/mureuchi/?nopool=1","fps":60.1,"worstMs":16.9,"frames":601}

ブラウザの描画は 60 回/秒で頭打ちになるので、余裕がどれだけあるかは fps には出ません。差を見るには、数を上限まで増やした場面(361個)で、1フレームにかかる時間そのものを測る必要がありました。

⚠️ 「fps 60 だから大丈夫」ではない

fps は頭打ちの数字です。余裕を知りたいなら、1フレームにかかった時間(や最長フレーム)を見てください。余裕が無い状態は、スマホなど遅い機械でそのまま処理落ちになります。

この数が出ている画面

ムレウチのプレイ画面。敵の三角が群れで降りてきて、白い弾が並んでいる
遊んでいる最中の画面。ここに出ているのは数十個で、この状態なら書き方の差は出ない

私は最初、絵の出し方を「毎フレーム作り直す」で書きかけました。数十個なら本当に何も起きないからです。使い回す形に最初からしたのは、あとから直すと描画まわりを書き直すことになると分かっていたためで、速さのためではありません。効き目の数字は、あとで測って初めて分かりました。

まとめ

  • 画面に361個ある場面で、19.06ms0.15ms・確保は1.9MB0バイト
  • 使い回すのは絵だけでなく、中身の配列も。消すときは最後のものを穴に移す
  • 効き目はテストで固定する(確保が増えたら落ちる)
  • ただし数十個の場面では差が出ない。fps ではなく、1フレームの時間で見る

FAQ

よくある質問

オブジェクトプールは、どれくらい効きますか?

このゲーム(画面に361個)では、描画の1フレームが 19.06ms から 0.15ms になりました。確保するメモリも 60フレームで 1,904,640 バイトから 0 バイトになります。ただし数十個しか出ない場面では、どちらの書き方でも 60fps でした。

何を使い回せばいいですか?

毎フレーム作り直しているものすべてです。GameObject や SpriteRenderer はもちろん、中身(ロジック)側で new している配列やリストも同じです。数が増減するものは、配列を固定の大きさで持ち、使う数だけ前に詰めます。

消すときはどうしますか?

消さずに隠します(SetActive(false))。中身の配列では「最後のものを穴に移して数を1減らす」形にすると、並べ替えも「消えた印」も要りません。

最初から凝ったほうがいいですか?

数が少ないうちは差が出ません。ただし「毎フレーム作って壊す」は後から直すと描画まわりを書き直すことになるので、数が増えると分かっているなら最初から使い回す形にしておくのが楽です。

この記事は役に立ちましたか?

NEXT