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

1 / 4 命中範囲を設定する ​

このステップの目的

素材の大きさ、画面での表示範囲、命中範囲を分け、命中判定に使う矩形を追加します。

3つの大きさを分けて考える ​

画像ファイルには素材本来のピクセル数がありますが、その大きさをゲームの表示や判定へそのまま使う必要はありません。
この教材では、次の3つを独立させます。

種類決まる場所役割
画像本来の大きさ画像ファイル素材が持つピクセル数
表示範囲GameTuning.hのDrawRect画面上で見せる大きさと位置の補正
命中範囲GameTuning.hのHitboxゲーム上で当たったとみなす範囲

画像の余白まで判定に含めると、触れていないように見えるのに命中することがあります。
表示範囲と命中範囲を分ければ、素材を加工し直さずに見た目と遊びやすさを個別に調整できます。

矩形同士の重なりを判定する ​

Rect.hの既存のstruct Rectの内側へ、中心座標を取得する関数とOverlaps()を追加します。既存の座標メンバやCentered()、MovedBy()は残してください。

Rect.hへ追加cpp
struct Rect
{
// 矩形の中心座標を返す
constexpr Vector2 GetCenter() const
{
    return { x + width * 0.5f, y + height * 0.5f };
}

// otherと面積を持って重なっていればtrueを返す
// 辺が触れているだけの状態は重なりとして扱わない
constexpr bool Overlaps(const Rect& other) const
{
    return
        x < other.x + other.width && other.x < x + width &&
        y < other.y + other.height && other.y < y + height;
}

Overlaps()は、2つの矩形がX方向とY方向の両方で重なっているかを調べます。両方が重なっている場合だけtrueを返します。

オブジェクトの命中範囲を定義する ​

プレイヤー弾と敵の命中矩形は、調整しやすいようにGameTuning.hへ定義します。どちらも、オブジェクトの中心座標を基準とした相対矩形で指定します。

GameTuning.hへ追加cpp
namespace playerShot
{
    // 弾の中心を基準とした命中矩形(幅8、高さ20)
    inline constexpr Rect Hitbox = Rect::Centered(8.0f, 20.0f);
}

namespace enemy
{
    // 敵の中心を基準とした命中矩形(幅32、高さ32)
    inline constexpr Rect Hitbox = Rect::Centered(32.0f, 32.0f);
}

続いて、PlayerShot.hとEnemy.hに、現在のワールド座標における命中範囲を返すメンバ関数GetHitbox()を宣言します。
それぞれの.cppでは、基準矩形を現在位置へ移動させた矩形(MovedBy(position))を返すように実装します。

PlayerShot.h、Enemy.hに追加cpp
public:
// 現在位置から求めた命中判定用Rectを返す
Rect GetHitbox() const;
PlayerShot.cppcpp
Rect PlayerShot::GetHitbox() const
{
    return tuning::playerShot::Hitbox.MovedBy(position);
}
Enemy.cppcpp
Rect Enemy::GetHitbox() const
{
    return tuning::enemy::Hitbox.MovedBy(position);
}

続いて、Enemy::Update()の画面外で停止する条件と、EnemyManagerの出現位置計算をそれぞれ次の例へ置き換えます。Hitboxの寸法を基準にするため、サイズを変更しても画面の端との位置関係が保たれます。

Enemy.cppcpp
// 敵の上端が画面下端を超えたら停止する
if (GetHitbox().y > tuning::ScreenHeight)
    Deactivate();
EnemyManager.cppcpp
// 敵の本体が画面上端のすぐ外側に隠れる位置から出現させる
Vector2 startPosition =
{
    tuning::enemy::LaneStart + lane * tuning::enemy::LaneSpacing,
    -tuning::enemy::Hitbox.height * 0.5f
};

管理クラスから走査用の配列を取得する ​

命中判定はGameクラスで行います。そのため、PlayerShotManagerとEnemyManagerから内部のオブジェクト一覧を安全に参照できるようにします。

外部から自由にアクセスされないよう、配列を返すGetAll()をprivateに置きます。そのうえで、Gameクラスだけをfriendに指定します。

PlayerShotManager.hcpp
private:
    friend class Game;

    std::array<PlayerShot, Capacity>& GetAll() { return shots; }
    const std::array<PlayerShot, Capacity>& GetAll() const { return shots; }
EnemyManager.hcpp
private:
    friend class Game;

    std::array<Enemy, Capacity>& GetAll() { return enemies; }
    const std::array<Enemy, Capacity>& GetAll() const { return enemies; }

friendの役割

friend class Game;は、Gameからこのクラスのprivateメンバへアクセスすることを許可します。ここでは、Gameが命中判定のために配列を走査できるようにします。

Overlaps() による重なり判定の呼び出し ​

Rect::Overlaps()は、2つの矩形が重なっているときにtrueを返します。
PlayerShotとEnemyの命中判定は次のように記述できます。

Game.cppcpp
if (playerShot.GetHitbox().Overlaps(enemy.GetHitbox()))
{
    // 命中したときの処理
}
チェックリスト
  • 画像本来の大きさ、表示矩形、命中矩形の役割の違いを説明できる
  • オブジェクトの中心座標を基準として相対的な矩形を生成できる
  • プレイヤー弾と敵が自身の現在位置に応じたGetHitbox()を返している
  • friend指定を用いて、Gameクラスからのみ安全に管理配列を参照できる
  • Rect::Overlaps()を用いて矩形同士の重なりを判定できる