Skip to content
第23回 トラップとゲーム進行

1 / 4 トゲトラップの危険判定と進入属性の分離

このステップの目的

タイル定義に「進入不可フラグ(isSolid)」と「危険領域フラグ(isHazard)」を独立して導入し、トゲトラップの進入可能性とミス判定を両立させる仕組みを実装します。

進入不可領域と危険領域の分離

アクションゲームにおける障害物には、プレイヤーの進入を物理的に遮る「壁や床」と、進入可能でありながら触れた瞬間にミスとなる「トラップ」の2種類が存在します。

これらを明確に区別するため、タイル定義構造体 TileDefinition に2つの独立した属性を持たせます。

Stage.hcpp
struct TileDefinition
{
    StageCell cell;
    ImageId imageId;
    dxgame::Rect drawRect;
    bool isSolid;           // 物理的に進入できないか(壁・床)
    bool isHazard;          // 触れるとミスになるか(トゲ等)
    dxgame::Rect hazardRect;// タイル内の危険判定領域
};

各タイルの設定例は以下の通りです。

セルの種類isSolidisHazard挙動
Block(通常ブロック)truefalseプレイヤーを押し戻し、上に乗ることができる
Spike(トゲトラップ)falsetrue押し戻さずに通過を許すが、接触するとミスになる
Goal(ゴール旗)falsefalse衝突も危険判定も行わず、クリア判定に用いる

トゲトラップで isSolid = false とする理由

もしトゲの isSolidtrue に設定すると、押し戻し処理(ResolveVertical)によってプレイヤーがトゲの上に着地して歩行できてしまいます。トゲトラップは接触した瞬間にミスとなる危険領域であるため、物理押し戻しの対象から除外(isSolid = false)し、危険判定のみを適用します。

見た目と判定の調整(hazardRect)

トゲの画像は、32×32ピクセルの正方形タイルの全面にトゲが存在するわけではありません。先端の周囲には透明な余白があり、根元には床との境目があります。

もしタイル領域全体(32×32)をそのまま危険判定にすると、トゲの先端をかすめるように通過しようとした際、透明な余白に触れただけでミスと判定され、見た目との乖離が生じます。

text
+---------------+
|   / \   / \   |  <- 透明な余白
|  /   \ /   \  |
| /     V     \ |  +-------------+
|/             \|  |  hazardRect | <- 実際の危険判定は一回り小さく設定
+---------------+  +-------------+

hazardRect の幅や高さをタイルのグラフィックよりも数ピクセル小さく内側に設定することで、視覚的な納得感の高い判定を実現します。

判定関数の責務分離

物理移動の押し戻しを行う ResolveHorizontal() および ResolveVertical()isSolid のみを検査し、トラップとの接触を検査する IsHazardAtRect()isHazard のみを検査します。

Stage.cppcpp
bool Stage::IsHazardAtRect(const dxgame::Rect& rect) const
{
    if (rect.width <= 0.0f || rect.height <= 0.0f)
        return false;

    const int leftTile = PixelToTile(rect.x);
    const int rightTile = PixelToTile(rect.x + rect.width - 0.001f);
    const int topTile = PixelToTile(rect.y);
    const int bottomTile = PixelToTile(rect.y + rect.height - 0.001f);

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (x < 0 || x >= mapWidth_ || y < 0 || y >= mapHeight_)
                continue;

            const TileDefinition* definition =
                FindTileDefinition(cells_[y][x]);
            if (definition == nullptr || !definition->isHazard)
                continue;

            const dxgame::Rect hazardRect =
                definition->hazardRect.MovedBy({
                    x * static_cast<float>(TileSize),
                    y * static_cast<float>(TileSize)
                });
            if (rect.Overlaps(hazardRect))
                return true;
        }
    }

    return false;
}

共通のタイル定義テーブルを一元管理することで、描画・物理衝突・危険判定の条件分岐を1箇所に整理します。

チェックリスト
  • タイル定義に進入不可フラグ(isSolid)と危険フラグ(isHazard)を分離して定義できる
  • トゲトラップが物理的に押し戻さず通過を許す理由を説明できる
  • 見た目との乖離を防ぐために危険判定矩形(hazardRect)を一回り小さく設定する理由を理解している

ゲームプログラミング実践