Skip to content
第04回 プレイヤーの弾と敵の命中判定

2 / 4 命中した弾と敵の処理 ​

このステップの目的

弾が敵に命中したときの処理をまとめ、敵の耐久力を減らして弾を消す仕組みを実装します。

敵に耐久力とダメージ処理を追加する ​

敵へ弾が当たったら、耐久力(HP)を減らします。まずHPを1、弾の攻撃力を1にして、1発で敵が消える処理を作ります。

敵の最大HPとプレイヤー弾の攻撃力は、調整しやすいようにGameTuning.hへ定義します。

GameTuning.hcpp
namespace playerShot
{
    // プレイヤー弾の攻撃力
    inline constexpr int Damage = 1;
}

namespace enemy
{
    // 敵の最大耐久力(HP)
    inline constexpr int MaxHp = 1;
}

続いて、敵を表すEnemyクラスにHPを保持するメンバ変数と、ダメージを受けるメンバ関数TakeDamage()を宣言します。

Enemy.hへ追加する宣言cpp
public:
    // 指定ダメージを受け、HPが0以下になったら停止する
    void TakeDamage(int damage);

private:
    int hp = 0;

敵が出現する際、HPを初期最大値へ設定します。Enemy.cppのSpawn()内で、調整値tuning::enemy::MaxHpを代入します。

Enemy.cppのSpawnに追加cpp
void Enemy::Spawn(const Vector2& startPosition, float speed, Behaviour spawnBehaviour)
{
    position = startPosition;
    baseX = startPosition.x;
    moveSpeed = speed;
    age = 0.0f;
    hp = tuning::enemy::MaxHp;
    behaviour = spawnBehaviour;
    isActive = true;
}

TakeDamage()では、受け取ったダメージ分だけhpを減らします。HPが0以下になったら、Deactivate()で敵を停止させます。

Enemy.cppへ追加する関数cpp
void Enemy::TakeDamage(int damage)
{
    hp -= damage;

    if (hp <= 0)
        Deactivate();
}

HPの変更と停止の判断をEnemyにまとめると、呼び出す側は与えるダメージだけを指定できます。

Gameから弾と敵へ処理を依頼する ​

Gameは弾と敵の両方を所有しています。両者の矩形を調べ、重なっていれば敵にダメージを与え、弾を停止させます。

命中判定の調停
Game
命中矩形を取得
PlayerShot
Game
命中矩形を取得
Enemy
Game
接触していれば TakeDamage() を呼び出す
Enemy
Game
接触していれば Deactivate() を呼び出す
PlayerShot

Gameクラスでは、有効なプレイヤー弾と敵のすべての組み合わせを調べます。矩形が重なっていれば、弾と敵それぞれに命中後の処理を行わせます。

プレイヤー弾の攻撃力定数を使用するため、Game.cppのインクルードにGameTuning.hを追加したうえで、衝突解決を行うResolveCollisions()を実装します。

Game.cppcpp
#include "Shooting/Game.h"
#include "Shooting/Tuning/GameTuning.h"

Game.hのprivate内で、Draw()の宣言の下へ次の宣言を追加します。その後、Game.cppのdxgame::shooting名前空間内へ関数を定義します。

Game.hcpp
    void Draw() const;
    void ResolveCollisions();
Game.cppcpp
void Game::ResolveCollisions()
{
    for (PlayerShot& playerShot : playerShots.GetAll())
    {
        if (!playerShot.IsActive())
            continue;

        for (Enemy& enemy : enemies.GetAll())
        {
            if (!enemy.IsActive())
                continue;

            if (!playerShot.GetHitbox().Overlaps(enemy.GetHitbox()))
                continue;

            enemy.TakeDamage(tuning::playerShot::Damage);
            playerShot.Deactivate();

            break;
        }
    }
}

Game::Update()で敵を移動した後、プレイヤーが次の弾を発射する前に呼び出します。ここまで編集したらビルドして実行し、弾が当たると敵と弾が消えることを確認します。得点は次のステップで追加します。

Game.cpp(Update内)cpp
    enemies.Update(deltaTime);

    ResolveCollisions();

    player.TryShoot(input, playerShots);

1発の弾が複数の敵へ貫通することを防ぐ ​

この二重ループでは、命中直後のplayerShot.Deactivate()とbreakが重要です。

弾が敵Aに命中した後も内側のループを続けると、同じフレームで近くや背後の敵Bとも命中判定を行います。通常弾が意図せず貫通しないよう、弾を停止させた直後にbreakで内側のループを抜けます。

これで、1発の通常弾が命中する敵を最大1体に制限できます。

チェックリスト
  • 敵自身がHPの保持およびダメージ時の停止処理を担当している
  • Gameクラスが仲介役として弾と敵の命中判定を調停している
  • 弾が敵に命中した直後にbreakし、同フレーム内に複数体へ貫通しないよう制御できている