1 / 4 命中範囲を設定する
素材の大きさ、画面での表示範囲、命中範囲を分け、命中判定に使う矩形を追加します。
3つの大きさを分けて考える
画像ファイルには素材本来のピクセル数がありますが、その大きさをゲームの表示や判定へそのまま使う必要はありません。
この教材では、次の3つを独立させます。
| 種類 | 決まる場所 | 役割 |
|---|---|---|
| 画像本来の大きさ | 画像ファイル | 素材が持つピクセル数 |
| 表示範囲 | GameTuning.hのDrawRect | 画面上で見せる大きさと位置の補正 |
| 命中範囲 | GameTuning.hのHitbox | ゲーム上で当たったとみなす範囲 |
画像の余白まで判定に含めると、触れていないように見えるのに命中することがあります。
表示範囲と命中範囲を分ければ、素材を加工し直さずに見た目と遊びやすさを個別に調整できます。
矩形同士の重なりを判定する
Rect.hの既存のstruct Rectの内側へ、中心座標を取得する関数とOverlaps()を追加します。既存の座標メンバやCentered()、MovedBy()は残してください。
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へ定義します。どちらも、オブジェクトの中心座標を基準とした相対矩形で指定します。
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))を返すように実装します。
public:
// 現在位置から求めた命中判定用Rectを返す
Rect GetHitbox() const;Rect PlayerShot::GetHitbox() const
{
return tuning::playerShot::Hitbox.MovedBy(position);
}Rect Enemy::GetHitbox() const
{
return tuning::enemy::Hitbox.MovedBy(position);
}続いて、Enemy::Update()の画面外で停止する条件と、EnemyManagerの出現位置計算をそれぞれ次の例へ置き換えます。Hitboxの寸法を基準にするため、サイズを変更しても画面の端との位置関係が保たれます。
// 敵の上端が画面下端を超えたら停止する
if (GetHitbox().y > tuning::ScreenHeight)
Deactivate();// 敵の本体が画面上端のすぐ外側に隠れる位置から出現させる
Vector2 startPosition =
{
tuning::enemy::LaneStart + lane * tuning::enemy::LaneSpacing,
-tuning::enemy::Hitbox.height * 0.5f
};管理クラスから走査用の配列を取得する
命中判定はGameクラスで行います。そのため、PlayerShotManagerとEnemyManagerから内部のオブジェクト一覧を安全に参照できるようにします。
外部から自由にアクセスされないよう、配列を返すGetAll()をprivateに置きます。そのうえで、Gameクラスだけをfriendに指定します。
private:
friend class Game;
std::array<PlayerShot, Capacity>& GetAll() { return shots; }
const std::array<PlayerShot, Capacity>& GetAll() const { return shots; }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の命中判定は次のように記述できます。
if (playerShot.GetHitbox().Overlaps(enemy.GetHitbox()))
{
// 命中したときの処理
}- 画像本来の大きさ、表示矩形、命中矩形の役割の違いを説明できる
- オブジェクトの中心座標を基準として相対的な矩形を生成できる
- プレイヤー弾と敵が自身の現在位置に応じた
GetHitbox()を返している -
friend指定を用いて、Gameクラスからのみ安全に管理配列を参照できる -
Rect::Overlaps()を用いて矩形同士の重なりを判定できる