Appearance
第18回 重力、ジャンプ、着地
2 / 4 床へ着地する位置を求める
このステップの目的
Stage で床の位置を調べ、Player の足元を床の上端へ合わせます。
Stageへ縦移動を渡す
Stage.h へ ResolveVertical(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になることもあるため、移動中の衝突だけには頼りません。
チェックリスト
- 床に落ちると、プレイヤーの足元が床の上面で止まる
-
StageとPlayerで移動量を二重に加えていない - 衝突したときにY速度と端数を0へ戻す