Appearance
第19回 地形との衝突解決
3 / 4 縦移動後の床と天井の衝突解決
このステップの目的
Y軸方向へ移動した命中矩形を調べ、落下時の「床着地」とジャンプ上昇時の「天井頭打ち」をそれぞれ適切な境界へ補正します。
床着地と天井頭打ちの判定分岐
垂直方向(Y軸)の移動では、移動方向によって衝突時の意味が異なります。
- 下向き移動(
moveY > 0:落下中):
足元が床ブロックに接触したため、床タイルの上端へ押し戻します。これは着地処理であり、接地フラグonGround_ = trueを設定します。 - 上向き移動(
moveY < 0:ジャンプ上昇中):
頭部が天井ブロックに接触したため、天井タイルの下端へ押し戻します。これは頭打ち(上昇停止)であり、接地ではありません。
Stage.cppcpp
bool Stage::ResolveVertical(dxgame::Rect& rect, int moveY) const
{
if (moveY == 0)
return false;
// 移動後の矩形だけを調べるため、1フレームでタイルを越える
// 極端な速度ではコリジョンを通り抜ける可能性がある。
// 極端な速度ではコリジョンを通り抜ける可能性がある
rect.y += static_cast<float>(moveY);
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);
bool hit = false;
float resolvedY = rect.y;
for (int y = topTile; y <= bottomTile; y++)
{
for (int x = leftTile; x <= rightTile; x++)
{
if (!IsSolidTile(x, y))
continue;
const float candidate = (moveY > 0)
? static_cast<float>(y * TileSize) - rect.height
: static_cast<float>((y + 1) * TileSize);
if (!hit ||
(moveY > 0 && candidate < resolvedY) ||
(moveY < 0 && candidate > resolvedY))
{
resolvedY = candidate;
}
hit = true;
}
}
rect.y = resolvedY;
return hit;
}天井接触時の挙動(落下への反転)
Player クラスの垂直移動関数 MoveVertical() では、衝突結果を受け取って垂直速度と接地状態を更新します。
Player.cppcpp
void Player::MoveVertical(const Stage& stage, float deltaTime)
{
remainderY_ += velocity_.y * deltaTime;
const int pixelMove = static_cast<int>(remainderY_);
remainderY_ -= static_cast<float>(pixelMove);
if (pixelMove == 0)
return;
dxgame::Rect movedRect = GetHitbox();
if (stage.ResolveVertical(movedRect, pixelMove))
{
velocity_.y = 0.0f;
remainderY_ = 0.0f;
}
position_.y = movedRect.GetCenter().y;
}天井衝突と接地の区別
垂直方向の衝突検知時に無条件で onGround_ = true を設定すると、天井に接触した際にも接地状態と判定され、空中で再ジャンプが可能になる不具合が発生します。
そのため、接地フラグの更新は必ず moveY > 0(下向き移動)の場合に限定します。
天井接触時に垂直速度 velocity_.y を 0 にリセットすることで、次フレームからは重力によって自然に落下へ転じます。
ステージ外(上下左右の境界)の扱い
Stage::IsSolidTile(x, y) では、ステージ範囲外に対する境界ルールを定めています。
- 左右端および上端の外側:
壁(IsSolid = true)として扱い、プレイヤーが画面外へ抜けるのを防ぎます。 - 下端の外側:
固体とせず空間(IsSolid = false)として扱います。これにより、穴に落ちたプレイヤーが空中で止まらずそのまま落下し、後の回で実装するミス判定(リスポーン等)に移行できます。
チェックリスト
- 下向き移動時はタイルの上端へ、上向き移動時はタイルの下端へ矩形が押し戻されている
- 天井に接触した際、上向き速度が0になって落下へ転じる
- 天井衝突時に誤って
onGround_ = trueが設定されないようガードされている - ステージ下端の外側が床判定されず、穴への落下が成立する設計になっている