Appearance
第17回 加速度、摩擦、移動の端数
1 / 4 位置の変化を速度として持つ
このステップの目的
位置を直接書き換える実装から、毎フレームの速度(velocity_)を状態として保持し、キー入力に応じて加速度を加算する物理モデルへと移行します。
速度を内部状態として保持する理由
第14回では、キーが押されている間、位置に対して直接 position_.x += direction * MoveSpeed * deltaTime を加算していました。
この方式では、キーを押した瞬間に最高速度に達し、キーを離した瞬間に停止するため、動きが硬くなります。
滑らかな加減速を表現するためには、「現在どの速さと向きで動いているか」を表す**速度(velocity_)**をプレイヤーのメンバ変数として記憶し、キー入力によってその速度を徐々に変化させる必要があります。
Player.hcpp
namespace dxgame::platformer
{
class Player
{
private:
Vector2 position_ = {};
Vector2 velocity_ = {}; // 現在の移動速度(ピクセル/秒)
};
}速度はゲームの調整値ではなく、毎フレーム刻々と変化する「プレイヤーの動的な状態」です。リトライ時やゲーム開始時には、位置とともに { 0.0f, 0.0f } へリセットします。
加速度(Acceleration)の積分
キー入力に応じて速度を変化させる計算は、等加速度直線運動に基づきます。
$$v = v_0 + a \Delta t$$
- $v_0$ : 前フレームの速度(現在の
velocity_.x) - $a$ : 加速度(
tuning::player::Acceleration) - $\Delta t$ : 経過時間(
deltaTime)
Player.cppcpp
void Player::Update(const Input& input, float deltaTime)
{
const bool moveLeft = input.IsHeld(Key::Left);
const bool moveRight = input.IsHeld(Key::Right);
if (moveLeft && !moveRight)
{
velocity_.x -= tuning::player::Acceleration * deltaTime;
}
else if (moveRight && !moveLeft)
{
velocity_.x += tuning::player::Acceleration * deltaTime;
}
else if (velocity_.x > 0.0f)
{
velocity_.x = std::max(0.0f,
velocity_.x - tuning::player::Friction * deltaTime);
}
else if (velocity_.x < 0.0f)
{
velocity_.x = std::min(0.0f,
velocity_.x + tuning::player::Friction * deltaTime);
}
velocity_.x = std::clamp(velocity_.x,
-tuning::player::MaxSpeed, tuning::player::MaxSpeed);
remainderX_ += velocity_.x * deltaTime;
const int pixelMove = static_cast<int>(remainderX_);
remainderX_ -= static_cast<float>(pixelMove);
position_.x += static_cast<float>(pixelMove);
}例えば、加速度が 1260.0f ピクセル/秒² である場合、右キーを1秒間押し続けると、速度は理論上 1260 ピクセル/秒 増加します。
1フレーム(約0.0167秒)ごとに加算される速度は $1260 \times 0.0167 \approx 21$ ピクセル/秒 となり、プレイヤーは徐々に速度を増していきます。
チェックリスト
- 位置を直接書き換えるのではなく、速度
velocity_を保持する理由を説明できる - 加速度 $\times \Delta t$ によって速度が毎フレーム徐々に増加する仕組みを理解している
- 左右のキー入力に応じて速度の正負が正しく変化していることを確認できる