· 

【Unity】ApplyRootMotionの有無を操作した際に起きた問題

Last Updated  2024.02.11


 内容的には前回のダウン回避作成時の延長になります。

 AnimatorにあるAnimationデータ(もうこの言い方がすでに解りづらいのですが…)の移動値を反映するか否かの設定。 

 「 ApplyRootMotion 」を、スクリプトで操作した際に陥った問題と、その原因、対処法についてです。

  

 

 本当に前置きの注意点として。

  

 原因の分析はあくまで「自分の考え」なので、本当にそれで合ってるのかは疑問ですし、対処法もいわゆる「民間療法」的なものになるので、ちゃんとした解説と対処法があれば自分が知りたいです。

 

 いえ、ホントに結構な時間調べてもそれらしいものが見つからなかったんですよ。

 もちろん自分のリサーチ力不足のせいなんでしょうけども。

起きる問題

 ApplyRootMotion をスクリプトを使ってオフにし、またオンにした際、次のアニメーション開始位置がキャラの現在表示位置とズレていて瞬間移動してしまう、というものです。

 打ち上げ攻撃を喰らった瞬間にAnimatorの「ApplyRootMotion」をオフ、そこからの動きは前回のようにスクリプトと物理挙動で制御して……

 

 ダウン回避のAnimationが終了→ニュートラル状態に戻ったら「ApplyRootMotion」をオンに。

 

 という流れを実装したのが上のgif画像になります。

 ニュートラルに入った瞬間に位置が変わってますね。

 「 ApplyRootMotion をオフにした段階での座標 」がAnimator内部に残り、オンにした段階のアニメーション(待機ポーズ)がその座標(打ち上げられた位置)から開始されてるみたいです。

前ステを被せるとキャラが貫通します
前ステを被せるとキャラが貫通します

 

 これの問題は見た目もそうなんですが「キャラの実際の位置がズレてる」のがより深刻です。

 エディターのScene画面上では「CharacterController」の位置がズレてないので、より原因をややこしいものにしてます。

 AnimationファイルのRootMotion設定については、取り扱ってるサイトは数多くあり、割と初期段階でその辺りの仕様については学習したので、自分も把握できてます。

 

 ここが原因なら極論、手当たり次第に試せばどれかの組み合わせでいい塩梅のものが見つかるんですが、残念ながらどのパターンを使っても思い通りには動いてくれません。

 これはもう、いちいちApplyRootMotionを切らなくてもいい様に、完全に移動値を削除したAnimationファイルに作り直すしかないかな……とも思ったんですが、それはそれで大変なんですよね。

 

 

 あと、画像の動きに違和感を感じた方もいらっしゃるでしょうが、2D的な表現だと前回みたいに「キャラを後ろに飛ばすだけ」で問題なかったのですが、3Dだとそれじゃダメで、攻撃を当てる角度が状況ごとに違ってくるので、1P側とは反対方向に飛ぶ方が、より自然な動きになります。

 

 そのための角度制御がRootMotionオンのままだと難しいので、スクリプトで制御したいのはそういった事情も含んでます。

原因を考える

 AnimatorをつけてるオブジェクトのAnimationファイル内の操作項目に「transform」や「rotation」が含まれていると「スクリプトで値を変化させても初期値に戻されてしまう」という、自分もカメラ関係で通ってきた仕様があります。

 

 実際は動いてるんだけどすぐ元の位置に戻されるので制御できないってヤツですね。

 

 多分今回の件もこれが関係してるのは推測できて、Animator内の「どこ」の「何に」その座標が格納されているのか、またその書き換えは可能なのかが解ればよかったのですが、残念ですが現時点だと有益な情報も成果も得られませんでした。

 

 それでも諦めきれない自分は、Animationファイルの修正や作り直しをする以上の時間かけて(人それを本末転倒という)「おや?」と思う現象にぶつかります。

CharacterControllerの更新

 判定がズレてるならCharacterControllerを一旦切って、RootMotionオンする直前に戻せば更新できるんじゃなかろうかと思い

//Animatorの移動値が切られてて
if (!animator.applyRootMotion)
{
    //待機状態だったら元に戻す
    if (animator.GetCurrentAnimatorStateInfo(0).IsName("Idle"))
    {
       characterController.enabled = false;
       characterController.enabled = true;
       animator.applyRootMotion = true;
    }
}

スクリプト的にはこうなります。一見「意味あるの?」って内容ですね。

ダメ元でこれをLateUpdate内で起動するようにします。

 結果……

 (´・ω・`)「!!」

 

 少なくとも瞬間移動はしなくなってます。

 即小パン被せても、ヒット時の判定はキャラが表示されてるところにありますね。

 とはいえ、一瞬(1フレーム以下)でも判定が消えるのは格ゲー的にどうなの?という話ですし(無敵時間って言い張ることは出来ますが)、CharacterControllerをスクリプトで直接弄るのはあまり推奨されてない、という文言を目にする機会も何度かありました。

 

 ただ、喰らい判定が小さくなったり大きくなったりするのは割とよくある仕様だし、普通は意図して組み込んでると思います。

 

 

 なので CharacterController のオンオフではなく、数字を変えても更新されるんじゃなかろうかと

characterController.center = new Vector3;

 でアレコレ調整しながら試してみたところ……

 前ステやダッシュを被せても違和感のない挙動になりました。

おわりに

背後からでもイメージ通りの方向と向きへ
背後からでもイメージ通りの方向と向きへ

 対処療法ではあるんですが、今のところ欲しい結果は得られ、新しい問題も起きていないので、現時点ではこれで対処していこうかと思います。

 

 ゲーム全体でみればたった一挙動のために、いろんなことを調べ、模索したわけですが……やっぱり自分がおかしいと感じるものを他人がおかしいと感じない道理もないですし、放置もできないんですよね。   

 基本的な実装が終わって以降は、こういった模索ばかりが続いてます。

 

 

 

 一番ベストなのは

 

 「スクリプトで動かすアニメーションは移動値を入れるか否か前もって設計しておくこと」

 

 というのを自分への戒めとして〆たいと思います。

 

 

 Animatorに関して色々調べた結果、ポーズ中でも文字を点滅(アニメーション)させられそうだったり、そういった副産物的な別の収穫はあったんですけどね。