ルートモーションを含んだ歩行アニメーションの正規化
ルートモーションって何?
この辺りを見よう。
まあ Unreal Engine も Unity も使わないケドね! Godot を使って話をしていくよ。
なぜ正規化が必要か
西暦 2020 年時点でのゲームエンジンにおいてルートモーションは、開発者の方で移動速度を調整する前提で実装されているように思う。アニメーションをブレンドした上で速度を一定にしたいなら、予めアニメーションの移動速度を揃えないといけない。
これからの時代、アバターをカスタマイズする過程で歩行モーションとかを自分でアップロードできるプラットフォームが出てくると、ルートモーションをそのまま使う場合、歩行速度に差がついてしまったり、斜めへの移動がおかしくなったり、スピードハック的な事も容易に可能になってしまう。アップロード時にガチガチのヴァリデーションをしてもいいけど、ユーザー側でいちいちアニメーションを調整しないとアップロードできないなんていうのも避けたい。だからどこかの過程で正規化を考える必要がある。
実装
移動軸の制限
前後へのアニメーションなら Z 軸以外への移動値を 0 に、左右へのアニメーションなら X 軸以外への移動値を 0 にする。こんなイメージ。蛇行なんかはそもそも歩行アニメーションとして不適切だと考えて切り捨てる。
体格差による速度制限
今回はアニメーションのスケルトンとモデルのスケルトンの hips
の高さの割合を使う1。背が低くなるほど歩幅が小さくならないと変なのは感覚的にも分かると思う。でもそうすると、同じアニメーションを使ってるのに移動速度が揃わない事になっちゃうから、背が低いほど早歩きになるようにする。
移動値による速度制限
アニメーションの再生時間と移動値のグラフはいろんな形をとりうる。とりあえず、アニメーションの最大移動値もしくは累計移動量のどちらかを用いて正規化すればいい。進んだあと同じ位置に戻ってくるような 1 周期で原点からの移動量が 0 になるようなアニメーションは一生その場から動く事はできないけど、蛇行と同様に歩行アニメーションとして不適切だと考えて切り捨てる。どうしても、ルートモーションがないと動けないのが嫌ならルートモーションを使わないというオプションを入れればで住み分けができると思う。そういう人は滑るしそもそもこの記事読む意味ないけど。
再生速度を考慮したブレンド
例えば前方と左方のアニメーションの再生速度がそれぞれ 1 と 3 だとしたら、斜め 45 度へ移動するアニメーションの再生速度は 2 にしたい。でも当然ながら前と左のアニメーション、すなわち移動値が異なるアニメーション同士の再生速度をどちらも 2 にしたものをブレンドするだけだと片寄ったアニメーションになる。そこでそれぞれのアニメーションの移動値を調整する必要が出てくる。具体的には腰から下……まあルートモーションだけでもいいから、二つのアニメーションの再生速度を揃えた時に移動値が等しくなるような係数を掛ける必要がある。
(補足) Godot 3.2.x におけるブレンドアニメーションの問題点
そもそも AnimationTree
周りがバグってたり実装が足りなかったりする。平面状でブレンドを視覚的に行える NodeBlend2D
は存在するけど、各アニメーションの同期が取れないという問題がある。同期がずれて同時に同じ足を出そうとしてピョンピョン飛び跳ねる姿は目も当てられない。結局、 NodeBlend2D
を使わずにまともにルートモーションを持ったアニメーションをブレンドして歩行するのを実現する為には以下の修正が必要だった。
- アニメーションが指定するボーンが 1 本でもスケルトン内にないとスケルトンそのものが回転するバグ
- NodeAdd の挙動がおかしい
- ルートモーションのシークや逆再生がきちんと実装されていない
- 動いている斜面上で KinematicBody が静止できない
Godot 4.0 でこの辺りがどうなるか分からないけど、少なくとも Godot 3.2.x ではそれなりにエンジン自体を改造しないと AnimationTree
や KinematicBody
は使えたもんじゃない。
結果
そこそこの実装ができたように見える。ルートモーションを含んだ歩行アニメーションをカスタマイズできるプラットフォームではこれがデファクトスタンダードになって欲しいね。え?フルトラ?
後書き
アニメーションの再生速度とは関係のないことになってくるけど、実はブレンド時に右足と左足がぶつからない為には、どっちの足を先に出すかというような事を考えないといけない。これもアニメーションを調整して吐き出しなおすというのは面倒だから、再生開始位置のオフセットをアップロード時とかクライアント上とかのどこかで制御できるようにしておいた方がいい。
(付録 1 )ブレンドを使った斜め歩きについて
ブレンドを使った斜め歩きは大きく分けて 2 種類ある。
それぞれに必要なアニメーションは以下の通り。
カニ歩き(ストラーフ)
- 腰を前に向けた状態でのニュートラル
- 腰を前に向けた状態での前方への歩行
- 腰を前に向けた状態での後方への歩行
- 腰を前に向けた状態での左方への歩行
- 腰を前に向けた状態での右方への歩行
カニじゃない歩き(腰を回す歩き方)
- 腰を前に向けた状態でのニュートラル
- 腰を前に向けた状態での前方への歩行
- 腰を前に向けた状態での後方への歩行
- 腰を左に向けた状態でのニュートラル
- 腰を左に向けた状態での左方への歩行
- 腰を左に向けた状態での右方への歩行
- 腰を右に向けた状態でのニュートラル
- 腰を右に向けた状態での右方への歩行
- 腰を右に向けた状態での左方への歩行
カニ歩きの方が用意するアニメーションが少なくて済むけど、速度が上がってくると不自然。腰を回す場合は上のアニメーションに加えて、斜め前から斜め後ろに遷移する時に切り返す処理が必要になる。つまりクォータニオンだと腰がねじ切れる事はないけど補完が変だったりするから、ブレンド平面を前と後ろで分けるような何らかの制御を入れた方がいいという事。
(付録 2 )アニメーションの再生速度の計算例
条件
モデル
- Hips の高さが 0.7 m
アニメーション
- Hips の高さが 1.25 m
- 移動値は 2.0 m
- アニメーションの長さは 1.2 s
問題
平均移動速度 1.6667 m/s となる再生速度を求めよ。
解法
アニメーションをモデルに適用したとき、 1.2 s での移動値は、
(0.7 [m] / 1.25 [m]) * 2.0 [m] = 1.12 [m]
秒速は、
1.12 [m] / 1.2 [s] ≈ 0.9333 [m/s]
目標と速度を合わせる為には、
1.6667 [m/s] / 0.9333 [m/s] ≈ 1.7858
∴ 1.7858 倍速
(付録 3 )ブレンド時、各アニメーションの移動係数の計算例
条件
ブレンド平面
- 横軸 X 、 縦軸 Y
- 線形ブレンド(非加算)
アニメーション A
- 移動方向 +Y (前方)
- 再生速度 2.0
- 移動量 2.0 m
アニメーション B
- 移動方向 +X (右方)
- 再生速度 1.0
- 移動量 4.0 m
入力
- (X, Y) = (0.7071, 0.7071)
問題
アニメーション A と アニメーション B をブレンドして、入力と同じ方向へ平均移動速度 4.0 m/s で移動させる。この時の再生速度を A と B の中間をとって 1.5 にする場合、アニメーション A と アニメーション B それぞれの移動値を何倍すればよいか求めよ。
解法
必要な 1 周期での移動量は、
4.0 [m/s] / 1.5 [s] = 2.666 [m]
原点 (0, 0)
とブレンド座標 (0.7071, 0.7071)
を結ぶ直線と X 軸のなす角は 45° ( Vector2.angle()
で取得できる)だから、2.666 m をそれぞれのアニメーションへ分解すると、
A: 2.666 [m] * sin(45°) ≈ 1.885 [m]
B: 2.666 [m] * cos(45°) ≈ 1.885 [m]
ブレンド値が 0.5 である事を考慮して2、
A: 1.885 [m] / (2.0 [m] * 0.5) = 1.885
B: 1.885 [m] / (4.0 [m] * 0.5) = 0.9425
∴ A: 1.885 倍、 B: 0.9425 倍
シルク・レニュー
挿絵: ひよ八