Unityでアニメーションの動きに合わせてキャラクター位置を決定する

今回はアニメーションの動きに合わせてキャラクターの手や足、体の位置を合わせる機能を作ります。

手や足の位置を合わせるにはIKを使って通常のアニメーションから独立して手や足の位置や角度を指定する事が出来ましたが、今回の場合は元々のアニメーションの再生位置によってその時の手や足の位置を特定の位置に持っていくという方法です。

例えば少し高い所に飛び乗るアニメーションがあって、手をついたところのアニメーション位置の時に手の部分を山の手をつく位置に持っていくということを行います。

IKの場合と違って元々のアニメーションはそのままでアニメーションの再生位置によってキャラクターの位置を決めます。

キャラクターの移動には

Unityで主人公をナビゲーション機能とマウスクリックで移動させる
Unityでキャラクターはナビゲーション機能で移動させるが、移動先の指定はマウスクリックで行えるようにします。ナビゲーション機能を使っているので障害物を避けて最適な経路で目的地に進んでくれます。

で作った機能を使います。

Unityでキャラクターの移動をプログラミングしてみる
キャラクターを移動させる為のスクリプトを作成し、キャラクターを動かす機能を作成します

のキー操作でも出来ますが一部違う場所もあります。
違う個所は機能のOn・Offをしている個所だけなのでその他は同じです。

キャラクターの移動は今回の機能を実現するのには関係なくキャラクターを通常通り動かす時に使うだけです。

スポンサーリンク

アニメーションの動きに手や足の位置を合わせる方法を考える

まずはアニメーションの再生位置によってキャラクターの手や足、体を特定の位置に移動させる方法を考えます。

IKの時と同じようにアニメーターコントローラーでIK Passのチェックを入れOnAnimatorMove関数内でキャラクターの位置を動かす事は出来ます。
しかし手や足の位置を合わせるのはなかなか難しそうです。

IKを使えば出来ると思いますが、結構大変そうなのと元のアニメーションを変更してしまいます。

こう考えていくと難しそうですが比較的簡単にアニメーションの再生位置によって手や足の位置を合わせる事が出来る機能があるんです。
それがターゲットマッチングという機能です。

Animator.MatchTarget関数を使えばアニメーションの再生位置に合わせてキャラクターを移動させる事が出来ます。

しかしこのターゲットマッチング機能はアニメーションの動きをキャラクターに反映させている場合だけ使えます。

わたくしがキャラクターの移動に関して行ってきたアニメーションの動作をキャラクターの動きに反映させない方法ではターゲットマッチングは使えません。

matching1.png

上の画像にあるAnimatorのApply Root Motionにチェックが入っていないと使えません。(使う方法あるかな?)
しかし最初からここにチェックを入れると今まで作成してきたキャラクター移動のスクリプトがうまく動作しなくなります。

ではどうすればいいんでしょうか?

ターゲットマッチングを使う時だけキャラクター移動のスクリプトを使わずAnimatorのApply Root Motionにチェックを入れることにします。

まずはターゲットマッチング関数の使い方を見てみます。

Animator.MatchTarget(到達の位置, 到達の角度, 到達させるボディーパーツ, ウエイト, 開始時間, 終了時間)

アニメーションの開始時間から終了時間の間で到達の位置と角度に到達させるボディーパーツ(手、足等)をウエイトの分だけ到達させます。
アニメーションの開始時間と終了時間はアニメーションクリップを見て調べます。

調べ方は後でやります。

この関数を使えばアニメーションの再生位置によって到達したい体のパーツを到達したい位置に移動出来ます。

キャラクターが登る山のゲームオブジェクト等を用意する

では今回の機能を作る為のゲームオブジェクトを用意します。

ターゲットマッチング2

JumpUpObjがキャラクターがジャンプして登る山です。

JumpTargetは右手が到達する位置です。アニメーションは変わらないので他のパーツの指定はしません。

SearchPlayerはキャラクターが山に登る為にジャンプが出来る範囲で、キャラクターがその範囲に入ったかどうかを調べます。

ターゲットマッチング3

上が出来上がったゲームオブジェクトです。
透明な緑色がSearchPlayerで白いブロックが登る山です。

飛び上がって地面を掴み登るアニメーションの開始時間と終了時間を調べる

それではキャラクターが山にジャンプしてつかまるアニメーションを用意し、ターゲットマッチングで使用する
アニメーションの開始時間と終了時間を調べます。

ターゲットマッチング4

使用するアニメーションは上のように飛び上がって地面を掴み登るアニメーションです。

ターゲットマッチング5

ターゲットマッチングの開始時間は足を離す直前にしました。
13%の所を開始時間にします。

ターゲットマッチング6

終了時間は手を地面について伸びあがる前の部分にしました。
30.2%の所です。

お好みにより開始時間と終了時間を変更してください。

ターゲットマッチングを使用したスクリプトMatchTargetMoveスクリプトを作成する

ターゲットマッチングに使用するアニメーションクリップの位置は調べ終わったので、実際に使用するスクリプトを作成します。

publicでjumpTagetを宣言し、インスペクタでゲームオブジェクトのJumpTargetを指定します。
ジャンプの再生位置は1より下の間繰り返します。

1回の再生ではnormalizedTimeは0から1の間の値になりますが、2回目からは1から2の間となります。

アニメーションクリップはループ再生させていませんが、1秒を超える値が入ってきたのでこの条件を加えました。

ジャンプ中はキャラクターの向きを到達点の向きに変えていきたいので、

transform.rotation = Quaternion.RotateTowards(transform.rotation, jumpTarget.rotation, 360 * Time.deltaTime);

Quaternion.RotateTowards関数を使用します。
最初に指定した角度から到達点の角度に1秒間で360度変更します。

navMove.enabled = false;
agent.enabled = false;
animator.applyRootMotion = true;
jumpFlag = true;
charaCon.enabled = false;

ジャンプ中はナビゲーション移動スクリプトのNavMove、ナビゲーションエージェント、CharacterControllerをOffにし、AnimatorのapplyRootMotionをOn、jumpFlagをOnにしています。

マッチターゲットでは到達位置、角度、到達させるボディーパーツ(ここでは右手)、ウエイト、開始時間と終了時間を指定しています。
到達位置と角度はインスペクタで設定したJumpTargetの位置と角度になります。

ボディーパーツは

AvatarTarget.パーツ

を使います。パーツの部分はRoot、Body、RightHand、LeftHand、RightFoot、LeftFootが指定出来ます。
IKのAvatarIKGoalと同じですね。

ウエイトはMatchTargetWeightMask関数を使います。

指定している引数は

MatchTargetWeightMask(Vector3(X, Y, Z)のウエイト, 角度のウエイト)

になります。

jumpFlagがOnの間はジャンプアニメーションの再生位置が80%を超えたらナビゲーション機能等を元に戻し、普通の移動が出来るようにします。

80%にしたのはジャンプアニメーションの最後の方の部分が終わるまで通常の移動が出来ないので、なるべく早めに通常の移動が出来るようにする為です。

80%をぎりぎりの98%等にするとジャンプし山に登ったあともしばらく通常の移動が出来なくなります。

キャラクター検知スクリプトSearchJumpUpを作成する

キャラクターのジャンプ機能が出来たので最後にSearchPlayerに設定するキャラクター検知スクリプトSearchJumpUpを作成します。

キャラクターを検知したらキャラクターに設置してるスクリプトMatchTargetMoveのJumpAreaとNotJumpArea関数を呼び出します。
進入時はJumpArea、出ていった時はNotJumpArea関数を呼び出します。

ターゲットマッチング機能で山をうまく登れるか確認する

これで機能が完成したので、Unityの実行ボタンを押して確認してみましょう。

ターゲットマッチング7

上が上からみた動画です。

SearchPlayer内でマウスの右クリックを押すと山にジャンプして掴まり登ります。
キャラクターが後ろ向きでも向きを変えちゃんと掴まっています。

ターゲットマッチング8

上の動画がアップです。

多少手が山に食い込んでいますが・・・・、これは各自調整してください(^_^;)
到達点の位置やターゲットマッチングの開始時間と終了時間の調整でうまく出来ると思います

これでUnityでアニメーションの動きに合わせて手の位置を山に合わせる機能が出来ました。
以前からターゲットマッチングの機能を使いたかったんですがどうしても使えなかったので放置してました。

アニメーションの動きに合わせて位置を合わせるのでApplyRootMotionのチェックが必要だったんですね。
チェックしなくても出来る方法もあるのかも!?

とりあえず出来た方法でやっていこう・・・・((+_+))