UnityのIKを使って物を運ぶアニメーション

今回はUnityのIKを使って物を運びながら走るアニメーションを作成してみようと思います。
岩や箱を一時的にでもキャラクターが持つという場面を考えます。

それ用のアニメーションを作成するとなると箱の大きさを考えて手の位置を変更し細かく調整する必要があります。

が、箱の大きさや持つ位置が合わないのでまた最初からアニメーションの調整!をしているとたまったもんじゃありません。

IKを使って右手、左手の位置や角度を指定すると元々のアニメーションにかかわらず、右手、左手、右足、左足の位置を特定の位置と角度に合わせる事が出来ます。

スポンサーリンク

IKを使用する為にアニメーターコントローラの設定を変更する

ik1

元々の走るアニメーションは上のような感じです。


ik2

キャラクターに設定しているアニメーターコントローラのBase Layerの歯車をクリックし、出てきたウインドウのIK Passにチェックを入れます。

これでスクリプトでOnAnimatorIK関数が呼ばれるようになります。

キャラクターが持つ箱を作成する

次にキャラクターが持つ箱を用意します。

ik3

上のように箱(Box)と右手と左手を置く位置をCreate Emptyで二つ(LeftHand、RightHand)作っておきます。

箱はキャラクターと一緒に動かしたいので子要素にしておきます。
まったく別物として扱うと難しくなるのでやりません・・・・。

最初は別オブジェクトとしておき、それを運びたい時は持ちあげるまでは別物として、持ち上げてからはキャラクターの子要素とした方が、運ぶというアニメーションだけなら楽かもしれません。

ik4

手の位置を設定するスクリプトIKを作成する

次にスクリプトから右手と左手の位置と角度をさきほど作成したBoxの右手(RightHand)の位置と左手(LeftHand)の位置に合わせるスクリプトを作成します。
スクリプトの名前はIKとしキャラクターに設定します。

publicで右手と左手の位置をインスペクタで設定出来るようにします。

右手、左手、右足、左足のIKは

AvatarIKGoal.名前

で取得出来、アニメーターの関数SetIKPositionWeight、SetIKRotationWeightでウエイト、
SetIKPosition、SetIKRotationで位置と角度の設定が出来ます。

OnAnimatorIK関数内でアニメーターの右手、左手の位置と角度のウエイトを1とします。
1にすると完全に指定した位置や角度になります。

IKに関しては

UnityのIKを使ってキャラクターがはしごを登るアニメーションを作る
UnityのIKを使ってキャラクターがはしごを登るようなアニメーションを作成していきます。

を参考にしてください。
実はこっちのはしごを昇る記事の方が難しいかも・・・・。

これでスクリプトが完成したので、次はUnityの実行ボタンを押して、細かい位置と角度を調整します。

手の位置と角度を細かく調整する

Unityの実行ボタンを押してGameウインドウタブからSceneタブに切り替えキャラクターをダブルクリックして近くによります。
Boxの子要素であるRightHandとLeftHandの位置と角度を調整します。RightHandの位置と角度を変更すると、リアルタイムで右手の位置と角度が変わります。

ik5

変更が終了したらインスペクタの歯車をクリックして、Copy Componentを選択します。
その後、実行を解除して、RightHandを選択しインスペクタの歯車をクリックしてPaste Component Valuesを選択します。

これで実行時のRightHandの位置が止まっている時のパラメータに設定されます。
右手と同じように左手のLeftHandの位置も変更します。

ik6

上のように右手と左手が固定され物を持って走るようになりました。

元のアニメーションで手が動いているので完全に合わせるのは難しいです。
これはアニメーターコントローラで物を持っている時は別のレイヤーで指先を動かさないものにすれば解決すると思います。

部分的にアニメーションを変更したい場合は、

Unityのレイヤー、アバターマスクを使って体の一部分を別のアニメーションにする
体の一部分だけのアニメーションを変更したい場合はAnimatorControllerのレイヤーとアバターマスクを使う事で実現出来ます。

を参考にしてください。

IKHintを使い関節の位置と角度を調整する

重い物を持っている時のように多少ガニ股にしてみます。
その為に膝の位置を変更します。

AvatarIKGoalは右手、左手、右足、左足の位置と角度ですが、
AvatarIKHintは右ひじ、左ひじ、右ひざ、左ひざの位置になります(位置というよりある程度方向を決めているのかも?)。

それではスクリプトに追加します。

追加する部分はほとんど同じなので大丈夫だと思います。
右手、左手の時と同じように膝の位置を設定します。

ik7

膝の少し前で少し外側にRightKneeを置きます。RightKneeはBoxの子要素に作成してください。
同じように左ひざLeftKneeを作成しUnityの実行ボタンを押して位置を調整してください。
右手や左手と違って前方の方にしないと曲がる時に膝が変な感じになるかもしれません。

これで膝の位置の調整も終わったので確認してみましょう。

ik8

見事にガニ股になりました。

これでIKを使って物を運ぶアニメーションが完成しました。
もう少し重量のある物を運ぶアニメーションにする場合は元のアニメーションで重心を下げておいた方が良さそうです。

もしくは重心の位置と角度を変更して対応します(追記の中でやり方を記載しています

また、運んでいる箱(シャレ?)がまったく動かないのでBoxも定期的に動かした方がリアルな感じが出ると思います。

箱を動かす機能を追加する

--以降追記--

物を持って動いている時は箱を少し動かしてみます。

膝のIKHintの位置をBoxの子要素にしていましたが、Boxを動かす時に膝の位置は変えたくないので、膝のIKHintの位置をキャラクターの子要素に
移動させます。

ik9

ヒエラルキーは上のようになります。手のIKの位置はBoxが動いた時に一緒に動かしたいのでそのままです。

次にBoxを動かすスクリプトShakeBoxスクリプトを作成します。

初期のBoxのローカル位置をbasePositionに入れ、そこにランダム値を入れて動かします。
なにやら難しそうな事をしていますが、やっている内容がわかればそんなに問題はないと思います。

アニメーターの現在の再生位置を取得するには
animator.GetCurrentAnimatorStateInfo(0).normalizedTimeを使います。

ik10

例えば走るアニメーションの左足が着地した時の再生位置は上のようにアニメーションが46.4%再生された時です。

animator.GetCurrentAnimatorStateInfo(0).normalizedTimeを取得すると0.464の値の時が左足が着地した時の位置になります。
その値を使ってBoxが移動するアニメーションの範囲を指定し、位置を設定しています。

0.464のような定まった値を取得するのは無理なので、ある程度範囲を広げて、その範囲内になったらflagをOnにして、1回しか設定しないようにします。
flagを使わないと範囲内に入っている時は連続で位置が指定されてしまいます。

normalizedTimeはアニメーションの再生位置を取得出来ますが、1回目の再生の時は0~1の間の値で取得出来ますが、2回目以降は整数値が足されていきます。

例えば2回目の0.464の位置になるとnormalizedTimeの値は1.464になってしまいます。
これではうまく値を取得出来ないので値の制限を加えて0~1の間に設定し直します。

normalizedTime = Mathf.Repeat(state.normalizedTime, 1);

Mathf.Repeatを使うと第1引数の値が0~1の間で設定されます。

transform.localPosition = Vector3.Lerp(basePosition, position, Time.deltaTime);

最後に現在の位置から指定した位置までなめらかに移動させます。

これでアニメーションと連動させて箱を動かす事が出来るようになります。
Unityの実行ボタンを押して試してみます。

ik11

少しだけリアルになったでしょうか?

Boxをただ動かしたいだけならば、走っているアニメーションの時だけBoxのローカルポジションをランダムに動かすだけでも、表現する事が出来ます。

あまり細かい所は気にしなくてもいい場合はそちらの方が簡単ですね。

重たいものを持っているようにキャラクターの重心を変更する

-- 以降さらに追記(重たい物を持っている時のように重心の位置と角度を変更) --

重たい物を持っている時はキャラクターの重心の位置と角度を調整して重そうに運ぶアニメーションに変更します。
IKスクリプトに重心の位置と角度をインスペクタで指定出来るようにします。

IKスクリプトのインスペクタで重心位置と角度を設定します。

実行ボタンを押して値を調整し重たいものを持っているように調整します。
重心の位置や角度を変更すると箱の位置が高くなりすぎる為、箱も少し下げておくといいです。

ik12

箱の位置は上のように調整しました。

ik13

IKスクリプトの重心位置と角度は上のように変更しました。

ik14

上が実行結果です。

少しだけ重い物を持っているように見えます。

UnityのIKを使って足の位置と角度を地面に合わせる機能
Unityのゲームでキャラクターの両足が別の高さの地面の上にある時にそれぞれの足をそれぞれの地面につけるようにする機能を作成します

と物を持っているIKの併用をするとおかしくなります。
なぜなら足のIKでも重心を下げる処理があるからです。

IKスクリプトとFootIKスクリプトを統合したり、他にもいろいろ対処が必要そうです。
気が向いたら対応します・・・・(-.-)