Skip to content
第02回 プレイヤーの弾とオブジェクトプール

2 / 4 停止中の弾を再利用する ​

このステップの目的

弾を配列に用意し、発射のたびに停止中の1発を使います。

弾を置く場所を先に用意する ​

PlayerShotManagerは48発分の弾を持ちます。発射時は停止中の弾を使い、画面外へ出た弾は停止状態に戻します。このように、用意したオブジェクトを使い回す仕組みをオブジェクトプール(Object Pool)と呼びます。

提供済みのクラス宣言を確認する ​

開始用のSource/Shooting/PlayerShotManager.hとPlayerShotManager.cppを開いてください。宣言とReset・Update・Drawは実装済みです。配列の所有と各関数の役割を読み、Spawnの未実装部分を記述します。

PlayerShotManager.hcpp
#pragma once

#include <array>
#include <cstddef>

#include "Shooting/PlayerShot.h"

namespace dxgame::shooting
{
    class ImageManager;

    // プレイヤー弾を固定数所有し生成、更新、描画、再利用を管理する
    class PlayerShotManager
    {
    public:
        // 停止中の弾の中心をstartPositionに合わせて生成する
        // 空きがない場合は生成せずfalseを返す
        bool Spawn(const Vector2& startPosition);

        // 所有するすべてのプレイヤー弾を更新する
        // deltaTimeは1フレームの経過秒数
        void Update(float deltaTime);

        // 所有するすべてのプレイヤー弾を描く
        void Draw(const ImageManager& images) const;

        // 所有するすべてのプレイヤー弾を停止して空の状態へ戻す
        void Reset();

    private:
        // 停止中の要素を再利用しゲーム中にはメモリを確保しない
        static constexpr std::size_t Capacity = 48;

        std::array<PlayerShot, Capacity> shots = {};
    };
}

std::array<PlayerShot, Capacity>は、PlayerShotをCapacity個並べた固定長配列です。Capacityはこのクラスで扱う上限数です。

空いている弾を探す ​

Spawn()は配列を先頭から調べ、最初に見つかった停止中の弾を発射します。発射できればtrue、全弾が使用中ならfalseを返します。

PlayerShotManager.cppcpp
bool PlayerShotManager::Spawn(const Vector2& startPosition)
{
    // 先頭から停止中の弾を探して再利用する
    for (PlayerShot& shot : shots)
    {
        if (shot.IsActive())
            continue;

        shot.Spawn(startPosition);
        return true;
    }
    return false;
}

PlayerShot& shotの&は参照を表します。shot.Spawn()による変更は、配列内の弾オブジェクトへ直接反映されます。

満杯の場合も試す ​

この確認は、ステップ4で移動と画面外停止を実装してから行います。今はビルドと、空きがない場合にfalseへ進む処理を確認します。

ステップ4の終了後、一時的にCapacityを2に変更して連射を試します。2発が画面内にある間は新しい弾が出ず、1発が画面外へ消えると次の弾を発射できることを確認します。確認後は48に戻します。

発展:メモリの確保と再利用

C++には、必要なときに領域を確保する方法もあります。確保と解放を頻繁に繰り返す場合は、処理にかかる時間と領域の管理を考える必要があります。プールは、確保済みの領域を再利用する方法です。

ただし、性能は処理内容によって異なります。領域の確保方法だけで速さが決まるわけではありません。要素数がデータから決まる場合は、std::vectorなども使い分けます。

チェックリスト
  • Spawnが最初に見つけた停止中の弾だけを発射する理由を説明できる
  • 全弾が使用中の場合はSpawn()がfalseを返す
  • 画面外での停止と再利用の実行確認はステップ4の後に行うと分かる