Skip to content

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

第01回では、画面上にプレイヤーを表示し、キーボード入力によって機体を移動させられるようにしました。
第02回では、プレイヤーが弾を発射する機能を実装します。

多数の弾を扱うため、弾1発を表す個体クラスと、弾全体をまとめる管理クラス(Manager)に役割を分けます。メモリ確保の負荷を抑えるオブジェクトプールの仕組みも学びます。

始める前に ​

クラスのメンバー関数、入力・更新・描画の流れを確認してください。確認したい場合は、プレイヤーの表示と操作を参照してください。

前の回の受講や完成物は必須ではありません。下の開始用プロジェクトを使い、配布済みの処理と今回追加する処理を確認してから進めます。

配布プロジェクト ​

第02回のプロジェクトを展開し、ShootingGame2D.slnをVisual Studio 2026で開いてください。
第01回で実装した自機の表示と移動操作が動作するプロジェクトから開始します。

配布ファイル一覧
ファイル役割
Source/Shooting/Game.cppゲームループと各オブジェクトの調停
Source/Shooting/Player.hプレイヤークラスの定義
Source/Shooting/Player.cppプレイヤーの移動処理および発射処理の実装
Source/Shooting/ImageManager.h画像IDとリソース描画機能
Assets/Images/Bullet.pngプレイヤーが発射する弾の画像

この回のゴール ​

ZキーまたはSpaceキーを押し続けることで、プレイヤーから弾を一定間隔で連射できる状態を目指します。

第02回の完成イメージ第02回の完成イメージ

  • 提供済みのPlayerShotを読み、位置・表示・有効状態の役割を説明できる
  • PlayerShotManagerの配列から停止中の弾を探すSpawnを実装できる
  • 提供済みの発射タイマーを使い、TryShootで連射間隔を制御できる
  • 画面外へ出た弾を自動的に停止させ、次の発射で再利用する「オブジェクトプール」のサイクルを完成できる

今回のポイント ​

プレイヤーの機体はゲーム中に1機しか存在しないため、Gameクラスが直接メンバ変数として所有しても問題ありません。
しかし、同時に多数飛び交う弾を1発ずつ個別の変数で管理することは現実的ではありません。

個別の変数で管理する好ましくない例cpp
class Game
{
private:
    PlayerShot shot0;
    PlayerShot shot1;
    PlayerShot shot2;
    // ... 弾が増えるたびに変数を増やすのは破綻する
    // ... 弾の増加に合わせて変数を増やすと管理が困難になる
};

複数の弾をまとめるには配列(std::array)を使えます。ただし、配列だけでは弾の寿命を調べたり、空きを探して新しい弾を発射したりできません。その処理までGameクラスに書くと、ゲーム全体のコードが複雑になります。

そこで、弾1発分の動作を扱うPlayerShotと、弾の配列を持って生成・更新・破棄を管理するPlayerShotManagerに役割を分けます。

弾の管理と委譲の流れ
Game
更新と射撃入力を処理
Player
Player
銃口座標を渡して弾の生成(Spawn)を依頼
PlayerShotManager
PlayerShotManager
空いている弾スロットを探して初期化
PlayerShot
Game
全体の更新(Update)と描画(Draw)を依頼
PlayerShotManager
PlayerShotManager
有効な弾だけを一括更新・一括描画
PlayerShot

このように役割を分けることで、Gameクラスは弾の内部構造や最大数を直接意識せず、ゲーム全体の制御を行えるようになります。

作業の準備 ​

開始用ZIPには、第01回の表示・操作に加え、弾のクラス宣言、画像登録、入力が用意されています。Gameからの呼び出しも接続済みです。新しいファイルを登録し直す必要はありません。開始直後は、弾が出ない状態でビルドできます。

段階編集する関数確認
ステップ1編集なし提供済みの弾の状態と描画を読む
ステップ2PlayerShotManager::Spawn停止中の弾を探して生成する処理を書き、ビルドする
ステップ3Player::TryShoot入力・待ち時間・生成成功を接続し、銃口で弾が発生することを確認する
ステップ4PlayerShot::Update移動と画面外停止を書き、連射と再利用を確かめる

未実装部分にはTODOがあります。boolを返す関数は、開始時にはfalseを返します。宣言、描画、配列全体の更新、発射タイマーを減らす処理は実装済みです。コード例でその役割を確認しますが、主に編集するのは表の3関数です。

ステップ2まではTryShootが未実装なので入力しても弾は出ません。ステップ3では弾が銃口で発生しますが、まだ移動せず、配列が満杯になると発射が止まります。ステップ4まで接続した後、Capacityを2へ変更し、画面外で停止した弾を再び使えることを確認します。確認後は自分が採用する値へ戻してください。

ヘッダー・登録表・呼び出し元は、最終ステップの参考実装と比較できます。GameTuning.hへ自分の調整を引き継ぐときは、該当する値だけを比較して反映します。

第02回の目次 ​