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

3 / 4 射撃入力と発射間隔の制御 ​

このステップの目的

射撃アクションを定義し、キーを押し続けた際に適切な間隔で弾を連射できる仕組みを実装します。

射撃アクションの追加 ​

第01回で導入した入力システムに従い、射撃操作も物理キーを直接調べずに抽象的な「アクション」として定義します。
今回はZキーとSpaceキーの両方を、弾を発射するAction::Shootに割り当てます。

開始用のInput.hにはAction::Shoot、Input.cppのキーバインド表には対応キーを登録済みです。次の対応を読み、TryShootへ渡る入力がどこから来るか確認します。

Input.hcpp
enum class Action
{
    MoveLeft,
    MoveRight,
    MoveUp,
    MoveDown,
    Shoot,      // 弾を発射するアクション
    Exit
};
Input.cppcpp
// ゲーム内の操作(Action)と物理キーの対応表
constexpr Binding Bindings[] =
{
    // ... 移動キーの定義 ...
    { Action::Shoot, KEY_INPUT_Z },     // Zキーで発射
    { Action::Shoot, KEY_INPUT_SPACE }, // Spaceキーでも発射
    { Action::Exit, KEY_INPUT_ESCAPE }
};

これにより、プレイヤーは好みに応じてZまたはSpaceのどちらを押しても弾を撃てるようになります。

発射間隔(連射クールダウン)の必要性 ​

シューティングゲームでは、発射ボタンを押し続けることで弾を連続発射(オート連射)できる操作が一般的です。
キーを押している間、毎フレーム弾を撃つと1秒間に60発になります。弾が画面を埋めてしまい、ゲームのバランスが崩れます。

そこで、前の発射から一定時間が過ぎるまでは次の弾を撃てないようにします。この待ち時間が発射間隔、またはクールダウン時間です。

発射判定とタイマーの流れ
射撃入力
発射可能時刻か確認
発射タイマー
発射タイマー
条件成立時に生成を依頼
PlayerShotManager
PlayerShotManager
生成成功時だけ基準時間へ戻す
発射タイマー

発射処理とタイマーの実装 ​

開始用のPlayerには発射タイマー変数shotTimerとTryShoot()の宣言があります。タイマーを減らすUpdateの処理を確認し、TryShootの本体を実装します。

Player.hcpp
public:
    // 入力とタイマーを確認し、弾を発射できたらtrueを返す
    bool TryShoot(const Input& input, PlayerShotManager& shots);

private:
    // 発射位置(銃口座標)を返す
    Vector2 GetMuzzlePosition() const;

    // 次の発射が可能になるまでの残り時間(秒)
    float shotTimer = 0.0f;

連射の間隔は、ゲームバランスを調整しやすいようにGameTuning.hへ定義します。
ここでは8フレーム分(8×160≈0.133秒)の間隔を設定しています。

GameTuning.hcpp
namespace player
{
    // 弾の発射間隔(秒)
    inline constexpr float ShotInterval = 8.0f * config::DeltaTime;
}

Player::TryShoot()では、射撃キーが押されており、かつshotTimerが0以下である場合のみ発射を試みます。

Player.cppcpp
bool Player::TryShoot(const Input& input, PlayerShotManager& shots)
{
    if (!input.IsDown(Action::Shoot) || shotTimer > 0.0f)
        return false;

    if (!shots.Spawn(GetMuzzlePosition()))
        return false;

    shotTimer = tuning::player::ShotInterval;
    return true;
}

タイマーをリセットするのは、弾の生成(shots.Spawn())に成功したときだけです。この条件が重要です。プールが満杯ならリセットせず、空きができたらすぐに次の弾を発射します。

タイマーの減算処理 ​

毎フレームの更新処理Player::Update()内で、経過時間deltaTime分だけタイマーを減算し、0未満にならないよう制限します。

Player.cpp の提供済み Updatecpp
// 発射タイマーを減算し、0未満には下げない
shotTimer = std::max(0.0f, shotTimer - deltaTime);
  • std::max:大きい方を選ぶ
  • 0.0f:下限の0秒
  • shotTimer - deltaTime:減算後の秒数

提供済みの銃口位置(発射座標)を読む ​

GetMuzzlePositionは提供済みです。TryShootへ渡す位置の意味を確認します。

弾はプレイヤーの機体のどこから発射されるべきでしょうか。
機体の中心点からそのまま発射すると、自分の機体に弾が重なって不自然に見えることがあります。

機体の中心より少し上の位置を、銃口位置(マズルポジション)として計算します。

Player.cppcpp
Vector2 Player::GetMuzzlePosition() const
{
    return
    {
        position.x,
        position.y - 32.0f
    };
}

この位置が弾の出発点になります。この段階ではUpdateが未実装なので弾はその場に残ります。自機を横へ動かしながら発射すると、異なる銃口位置に弾が現れます。移動と画面外での再利用は次のステップで実装します。

チェックリスト
  • ZキーおよびSpaceキーがAction::Shootに正しく割り当てられている
  • 射撃ボタンを押し続けた際に、shotTimerによって一定の間隔で発射が制御されている
  • 弾の生成に成功した場合のみタイマーがリセットされる因果関係を理解している
  • 機体の先端(銃口位置)から弾が発生していることを確認できる