Appearance
第20回 アニメーションと音声イベント
2 / 4 物理状態に基づくアニメーション制御
このステップの目的
プレイヤーの物理状態(接地フラグおよび速度ベクトル)に基づいて、待機・走行・上昇・落下の4状態を優先順位に従って判定し、適切なアニメーションコマを進行させる制御を実装します。
状態判定の優先順位の整理
キャラクターのアニメーションは、現在の物理的な運動状態を視覚的に伝えるフィードバックです。複数の条件が同時に成り立つ可能性があるため、判定の優先順位を明確に定義します。
- 空中状態の判定(最優先):
接地フラグがオフ(!onGround_)である場合、プレイヤーは空中にいます。水平移動入力があっても空中走行のアニメーションにならないよう、垂直速度(velocity_.y)を基準に判定します。velocity_.y < 0.0f:上昇中(ジャンプ姿勢)velocity_.y >= 0.0f:下降中(落下姿勢)
- 地上状態の判定:
接地している場合のみ、水平速度の大きさ(std::abs(velocity_.x))を検査します。- 速度が閾値(デッドゾーン
0.1f)を超える場合:走行 - 速度が閾値以下の場合:待機
- 速度が閾値(デッドゾーン
Player.cppcpp
PlayerAnimation nextAnimation = PlayerAnimation::Idle;
if (!onGround_)
{
nextAnimation = velocity_.y < 0.0f
? PlayerAnimation::Jump
: PlayerAnimation::Fall;
}
else if (std::abs(velocity_.x) > 0.1f)
{
nextAnimation = PlayerAnimation::Run;
}
else
{
nextAnimation = PlayerAnimation::Idle;
}アニメーション切り替え時のタイマーリセット
アニメーション状態が別の状態へと遷移した瞬間には、コマ送り用のタイマーとフレーム番号を先頭(0)へリセットします。
Player.cppcpp
if (nextAnimation != animation_)
{
animation_ = nextAnimation;
animationFrame_ = 0;
animationTimer_ = 0;
}タイマーをリセットする理由
タイマーをリセットせずに引き継ぐと、走行アニメーションの終了直前にジャンプへ移行した際、次の瞬間に予期せぬコマ送りが発生したり、着地した瞬間に走行の途中の足の形から再開してしまいます。
状態が切り替わった瞬間に先頭フレームから再生することで、操作に対する視覚的応答の一貫性を保ちます。
コマ送り制御とスプライトシート番号へのマッピング
アニメーションの進行は、状態ごとに設定された表示時間(frameDuration)とコマ数(frameCount)に基づいて制御します。
Player.cppcpp
animationTimer_++;
if (animationTimer_ >= frameDuration)
{
animationTimer_ = 0;
animationFrame_ = (animationFrame_ + 1) % frameCount;
}描画を行う際には、GetAnimationFrame() が現在のアニメーション状態とフレーム番号をスプライトシート上の絶対フレーム番号(0〜7)へ変換します。
Player.cppcpp
int Player::GetAnimationFrame() const
{
switch (animation_)
{
case PlayerAnimation::Idle:
return animationFrame_;
case PlayerAnimation::Run:
return 2 + animationFrame_;
case PlayerAnimation::Jump:
return 6;
case PlayerAnimation::Fall:
return 7;
}
return 0;
}描画処理(Player::Draw)はこのマッピング関数を呼び出すことで、スプライトシート内のコマ配置に依存せずに描画できます。
チェックリスト
- 空中(上昇・落下)と地上(走行・待機)の優先順位に従ってアニメーション状態を判定できる
- 状態遷移時にタイマーとフレーム番号を0へリセットし、不自然なコマ飛びを防ぐ理由を説明できる
-
PlayerAnimationとローカルフレーム番号からスプライトシート上の絶対フレーム番号を導出できる