UnityのIKを使って足の位置と角度を地面に合わせる機能

今回はキャラクターの足の位置と角度を地面に合わせる機能を作ってみたいと思います。

今回

Let's get our selves acquainted with Unity's build-in IK System. Topics: Foot-IK (09:45) Foot Placement (41:30)Look-IK (52:47)Hand-IK Stay up to date. Subscr...

こちらの動画を参考にさせていただきました。

IKに関しては

UnityのIKを使って物を運ぶアニメーション
UnityのIKを使ってキャラクターが物を運んでいるアニメーションを作成していきます

の記事を参考にしてください。


まずは足の位置や角度を地面に合わせるやり方を考えます。

足の位置をアニメーションとは別に地面に合わせる必要があるのでIKの機能を使います。

地面に位置と角度を合わせるので、足が地面と接地している個所を調べる必要があります。
その為にPhysics.Raycastを使ってキャラクターの足から下にレイを飛ばし常に接地面を調べるようにします。

スポンサーリンク

足からレイを飛ばし地面の情報を取得するスクリプト

既存のキャラクターに足のIKを施すスクリプトFootIKを新しく作り取りつけます。

手のIKを使った時と同じようにキャラクターにレイを飛ばす位置を設定し、そこからレイを飛ばし地面と当たった位置と角度に足のIKを設定します。

ここまでは手のIKの時と同じ事をしています。

足のIKの場合、立っているアニメーションの時はレイの当たった位置にIKのウエイトを1にして位置を設定すればいいんですが、歩いている時や走っている時に常にウエイトを1にしているとヒザ神(ロンドンハーツから引用)のような動きになってしまうのでアニメーションの動きにしたがってウエイトを変更していく必要があります。

この処理は後でやります。

まずはスクリプトのインスペクタ上で右足用のレイと左足用のレイを飛ばす位置を設定します。
それぞれ変数名はrightIKとleftIKとしてあります。

footik1

上のようにEthanLeftFootとEthanRightFootの子要素として空オブジェクトのRightFootRayとLeftFootRayを作成します。

footik2

LeftFootRayの位置は上のようになります。

これでレイを飛ばす位置の設定が終了したので、次にアニメーションの再生位置によってIKのウエイトを変更する処理を加えます。

アニメーションの再生位置によってIKのウエイトを変更する

まずは立っている状態のアニメーションIdleの場合は常にウエイトを1にして、レイの当たった位置に足を合わせるようにします。

アニメーションのCurveを使うとアニメーションの状態によってアニメーションパラメータの値を変更出来ます。
またアニメーションパラメータの名前と同じ名前をCurveの名前に指定すれば、アニメーションパラメータの値にCurveの値がそのまま設定されます。

アニメーションカーブに関しては

Unityのキャラクターコライダのサイズをアニメーションに応じて調整する
Unityで使う当たり判定(コライダ)のサイズをアニメーションに応じて変更し、ジャンプ中のキャラクターのコライダのサイズを変更し狭い空間を通過出来るようにしてみます。

を参考にしてください。

まずはアニメーションパラメータを作成します。

footik5

上のようにRightFootWeightとLeftFootWeightと名前を付けます。

footik3

次にIdleの状態のアニメーションを選択します。

footik4

上がIdleに設定しているアニメーションです。これを選択し、インスペクタ上のCurveを作成します。

footik6

Curvesの+の部分をクリックしRightFootWeightとLeftFootWeightを作成します。
ここでつける名前はアニメーションパラメータと同じ名前にします。

値の部分を1と設定し、常に1になるようにします。
これでIdleのアニメーションのCurveの設定は終了です。

次に走るアニメーションのCurveを設定します。

footik7

上のようにアニメーションの動きが見えるようにウインドウを動かしておきます。

Idleのアニメーションの時と同じようにCurveにRightFootWeightとLeftFootWeightというパラメータを作成します。
次に足の着地時はウエイトを1に、離れた時はウエイトを0になるようにCurveのパラメータを変更します。

まずは右足の着地時にRightFootWeightのウエイトを1にします。
アニメーションで右足全体が着地した地点を探します。

footik8

その部分を見つけたら、RightFootWeightのKeyを足すアイコンをクリックし、パラメータを1に設定します。
今回の場合はアニメーションの最初に右足着地しているので、Keyの追加をする必要はないかもしれません。

次に右足が離れる部分までアニメーションを移動します。

footik9

右足が離れる部分でRightFootWeightのKeyを追加し、パラメータを0に設定します。
右足が離れているアニメーションの間はウエイトを0に設定し、着地したら1に設定します。

足が空中にある間から着地までのちょっとの間はウエイトが0から1になめらかに遷移するようにします。
右足の設定が終わったら、左足の設定も同じようにしてください。

設定が終わったらUnityの実行ボタンを押してください。
足のIKがうまくいくか見てみましょう。

footik13

確認の為にIdle状態の時にRunのアニメーションを設定します。(キー操作なくアニメーションの動きを確認する為)
キャラクターをコピーしFootIKをOnにしたキャラクターとOffにしたキャラクターのアニメーションを比較し、本来のアニメーションと差異がないようにCurveのパラメータを設定していく必要があります。

設定が終わったらIdleのアニメーションをIdleに戻し、IKの部分を見ていきましょう。

出来たと思ったのに足が地面に届かない

footik10

んー、まったく出来ておりません・・・・(+_+)
右足は何となくレイの当たった位置に固定されているようですが、左足がレイの当たった位置に到達しません。

キャラクターを動かすスクリプトをOffにして、
キャラクターを選択し、TransformのYの位置を下げてみます。

footik11

上のように左足もうまく接地しました。

今度は地面をキャラクターを押し上げる感じで下から上に移動させます。

footik12

上のように足の位置が接地し、角度も地面に合っています。

つまり最初の画像ではRayが地面に当たっていてIKの設定もうまくいっているはずなのに、なぜうまく接地しないのかというと体の重心が右足部分を基点にしているから左足は伸ばしているけど地面に到達しない為です。

下から地面を上げた場合は到達点をキャラクターの足によせてるので問題が出ません。

足が地面に到達しないのはコライダの影響かも

ここからが問題です。

左足を地面に接地するにはキャラクターの重心を下げる必要があります。
重心を下げるにはAnimatorを入れる変数名をanimatorとしていた場合、

animator.bodyPosition

を変更します。

bodyPositionが変更出来るのはOnAnimatorMove関数内だけです。
また重心は右足と左足の距離分だけ下げてみます。

今度はキャラクターが沈み込んでしまう問題が発生

右足と左足のY座標の位置の差から重心を単純に設定していますが坂を登らせて見ると、
キャラクターが沈むような感じになってしまいます。

坂に右足だけをひっかけ、左足を地面に接地した状態でも不具合が発生します。

レイを飛ばす位置であるRightFootRayとLeftFootRayの置き場所をEthanRightUpLeg、EthanLeftUpLegの子要素にすると沈み込みがなくなります。

footik14

上の画像で
左がEthanRightUpLeg、EthanLeftUpLegの子要素にRayの位置を合わせたもの、
右がEthanRightFoot、EthanLeftFootの子要素にRayの位置を合わせたもの、
になります。

この違いがなぜかわかりません・・・(+_+)
レイを飛ばす開始位置で変わってしまいます。

走っている時に沈み込みが発生するので走っている時はbodyPositionを変更するのをやめてみます。

footik15

上のように走っている間は重心を下げないようにしたので、通常通りの走りになりました。
重心を下げないようにする処理は後で記述します。

沈み込みがなくなったので次の問題を解消します。

立っている状態なのに歩きのアニメーションにつられ片足が違う位置で接地してしまう

footik16

Idle状態で足が開いた状態で静止してしまっています。
Idle状態の時は常に足のIKのウエイトを1にしているのでRunのアニメーションからIdleのアニメーションへと遷移する段階でRayが当たった位置にIKで足の位置が設定されている為と思われます。

これは前述したRayを飛ばす位置をEthanRightUpLeg、EthanLeftUpLegにすると解消出来ます。
足の動きにかかわらず足の付け根辺りから真下にRayを飛ばすのでRunからIdleへアニメーションが遷移しても大丈夫です。

両足のレイは地面に到達しているのに両足が空中に浮いてしまう

次の問題です。
両足のIKがRayを飛ばした位置に設定されるが足が接地出来ないケースです。

footik17

上のようにコライダが階段の位置で接地し足が地面に到達しません。

これはどうしようかと思ったんですが、キャラクターのtransform.position.yの位置とRayが到達した地点で差異がある時はキャラクターのtransform.position.yの位置を変更してしまい、中途半端な位置にならないようにします。

この対処法はちょっと強引かもしれませんが・・・・処理は後で記述します。

足を置いている位置の高さが左右で違いすぎる場合、体が変に曲がってしまう

次の問題は右足と左足の位置が離れすぎた場合です。

footik18

上のように右足と左足の位置が離れすぎて足が変な感じで曲がってしまう時の対処をします。
これは右足と左足の差が範囲内の時は重心を下げ、それ以外は重心を下げず無視するようにします。

また足のIKの位置と重心の調整をする為の変数をpublicで宣言し、インスペクタから設定出来るようにします。

足のIKの問題点を解消する

それでは、これらの処理をスクリプトに追加していきます。

これでスクリプトが完成したのでUnityの実行ボタンを押して確認してみます。

footik19

まずはコライダが邪魔して足が空中に浮いてしまう時の対処が出来てるかどうか確認します。
浮いている状態になったらキャラクターのtransform.position.yを移動し、強制で浮きを解消しています。

footik20

次は右足と左足のIKの位置が大きくなりすぎた時の対処です。
一定の距離を超えたら重心を下げないようにします。
片足が浮いてしまいますが、これは仕方ないかも・・・・

これでIKを使って足の位置と角度を地面に合わせる機能が完成?しました。
すべてがうまくいった!という感じはしないですが・・・(^_^;)
ある程度は出来たんではないかと思います。