Skip to content
第18回 重力、ジャンプ、着地

2 / 4 床へ着地する位置を求める

このステップの目的

Stage で床の位置を調べ、Player の足元を床の上端へ合わせます。

Stageへ縦移動を渡す

Stage.hResolveVertical(dxgame::Rect& rect, int moveY) const を宣言します。moveY は端数処理から取り出した整数の移動量です。Stage 側が矩形を移動するため、呼び出す前にY座標へ同じ移動量を加算しないでください。

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;
}

PixelToTileはピクセル座標をタイル番号へ変換します。右端と下端から0.001を引くのは、境界線の先にある隣のタイルを含めないためです。IsSolidTileは指定したマスが床かどうかを返します。これらの補助関数もStageへ追加します。

Stage.cppcpp
int Stage::PixelToTile(float pixel)
{
    return static_cast<int>(std::floor(pixel / TileSize));
}

bool Stage::IsSolidTile(int tileX, int tileY) const
{
    if (tileX < 0 || tileX >= mapWidth_ || tileY < 0)
        return true;
    if (tileY >= mapHeight_)
        return false;

    const TileDefinition* definition =
        FindTileDefinition(cells_[tileY][tileX]);
    return definition != nullptr && definition->isSolid;
}

補正した位置をPlayerへ戻す

MoveVertical() は移動と補正の後、矩形の中心を position_.y へ戻します。衝突したときは速度と端数を0にします。

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;
}

接地状態は次のステップで足元を調べて確定します。床に立っているフレームでは移動量が0になることもあるため、移動中の衝突だけには頼りません。

チェックリスト
  • 床に落ちると、プレイヤーの足元が床の上面で止まる
  • StagePlayer で移動量を二重に加えていない
  • 衝突したときにY速度と端数を0へ戻す

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