6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rustでアニメーションをハードコーディングするノウハウ

Last updated at Posted at 2020-04-20

1. ゲームのアニメーション(3Dゲーム)

作るには主に以下の2つの方法があると思います。

1.1 アニメーションのファイルを作成する

普通はこの方法で作ります。オブジェクトをモデリングするときにアニメーションも一緒に作成してゲームに読み込みます。ゲームでは、キャラごとにジャンプや走るなどのアニメーションを作成し、それをプログラムで制御するという方法で実行します。

1.2 ハードコーディングする

プログラマーの意匠を物体の動きに盛り込みたい時にこちらの方法を使います。例えば草の揺れ動きとか水の流れなど、動きが一定ではなくランダムに動くアニメーションはコードで作成する方がより自然な動きを実現できます。。

2. ハードコーディングするメリット・デメリット

2.1 メリット

  • ランダム性のある動きを表現しやすい
  • データの容量節約になる
  • かなりプログラマーの自由が効く
  • 楽しい(重要)

2.2 デメリット

  • 人の動きなどの複雑な動きは難しい
  • 微調整が面倒
  • コードが大きくなりがち

3. Rustでアニメーション作成

ようやく本題です。Rustでアニメーションを作るときに私はよくステートマシーンを用います。このときEnumを用いて状態を管理します。基本的な作り方は、前フレームからの時間経過を取得し、現在の状態を実行し、終了したら次の状態に移るという形になります。

3.1 状態の定義

例えば走る→ジャンプ→攻撃というアニメーションを作成したい時はまず以下のように4つの状態をEnumを用いて定義します。かっこの中の数字は各状態のアニメーションの経過時間を保持します。
最初の状態は"走る"なので、現在の状態にRunを設定します。

state.rs
/// アニメーションの状態の種類
enum AnimationState {
    Run(f32), // かっこの中の数字は経過時間
    Jump(f32), 
    Attack(f32),
    Still // 何もしない状態
}

let current_state = AnimationState::Run(0.);

3.2 前フレームからの時間経過を取得

前フレームから経過した時間を取得し、現在状態が保持している経過時間に足し合わせます。このとき、tickという関数をAnimationStateに実装します。このtickという関数で状態遷移も定義するとスッキリしたコードになります。

system.rs
/* 前フレームからの時間経過を取得するシステム */
let dt: f32 = time.delta_seconds(); // Amethystはこう書きます
current_state.tick(dt);

state.rs
impl AnimationState {
    /// 状態が保持している経過時間に足し合わせる
    fn tick(&mut self, dt: f32) {
        *self = match self {
            AnimationState::Run(t) => {
                // tは状態の経過時間
                if *t >= RUN_DELAY {
                    // 走る状態が終了、ジャンプに移行
                    AnimationState::Jump(0.0)
                } else {
                    // 走る状態が継続中
                    AnimationState::Run(*t + dt)
                }
            }
            AnimationState::Jump(t) => {
                if *t >= JUMP_DELAY {
                    // ジャンプ状態が終了、攻撃に移行
                    AnimationState::Attack(0.0)
                } else {
                    // ジャンプ状態が継続中
                    AnimationState::Jump(*t + dt)
                }
            }
            AnimationState::Attack(t) => {
                if *t >= Attack_DELAY {
                    // 攻撃状態が終了、何もしない状態に移行
                    AnimationState::Still
                } else {
                    // 攻撃状態が継続中
                    AnimationState::Attack(*t + dt)
                }
            }
            AnimationState::Still => AnimationState::Still,
        }
    }
}

3.3 現在の状態を実行

現在の状態は経過時間を保持しているのでそれを元に進行状況(progress)を計算しオブジェクトを移動させます。

animation.rs
match current_state {
    AnimationState::Run(t) => {
        let progress = t / RUN_DELAY;
        /* progressを元にxy座標を移動 */
    }
    AnimationState::Jump(t) => {
        let progress = t / JUMP_DELAY;
        /* progressを元にz座標を更新 */
    }
    AnimationState::Attack(t) => {
        let progress = t / ATTACK_DELAY;
        /* progressを元に攻撃させる */
    }
    _ => ()
}

3.4 次の状態へ移行

AnimationStateのtickが自動で状態を遷移してくれます。

3.5 アニメーションの終了

状態がAnimationState::Stillまで到達したらアニメーションは終了です。同様のアニメーションをもう一度実行したいときはcurrent_stateにもう一度"走る"状態をセットするだけです。

4. まとめ

以上、ステートマシーンを活用したアニメーションのハードコーディングの方法の一例でした。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?