UnityのIKを使って物を押せるようにする

今回は何か物があった時に、キャラクターがそれを押す事が出来るようにしたいと思います。

物を押すアニメーションを作成し、押している間はそのアニメーションにしてもいいのですが、今回は普通に走っているアニメーションを使い、両手を物に合わせて押しているようにします。

スポンサーリンク

物を押すスクリプトPushOtherを作成する

それでは物を押すスクリプトPushOtherを作成します。
PushOtherは主人公に設定してください。

右肩と左肩付近にRightHandRayとLeftHandRayを作成し、外側の上辺りに0.5mの長さのレイを飛ばします。
もしそこでBlockレイヤーに指定したゲームオブジェクトに当たっていた場合はその位置を保存します。

rightRotation = Quaternion.FromToRotation(Vector3.up, hit.normal) * transform.rotation

としている部分でhit.normalはレイが当たった面の角度で、Vector3.upがY軸の上向きで、
Quaternion.FromToRotationでその間の角度を算出出来ます。

それに元々のQuaternionであるtransform.rotationをかける事で、元の角度も加味する事が出来ます。

正直、難しい所はわかりませんが・・・(^_^;)
あとで試す時に、キャラクターの手がBlockを触っている時にBlockの角度を変更してみるとわかりやすいかもしれません。

右肩からのレイがBlockレイヤーに当たっていたらnearRightをOnにします。

左肩からのレイの処理も右肩と同じです。

最後に右手か左手からのレイがBlockに当たった時は主人公のアニメーションパラメータPushをOnにします。
アバターを手の部分とIKだけ有効にしてアニメーターコントローラのレイヤーFingerを作成し、アニメーターのPushは手を開いている状態のクリップを用意します。

キャリー6

アバターは上のように設定します。

キャリー7

FingerレイヤーはPushがtrueになった時にIdleからPushに遷移させます。

キャリー8

PushからIdleに戻る時はPushがfalseになった時で、Has Exit Timeのチェックを外しておきます。

そして、Blockを押している間は攻撃や射撃の動作をさせない為にMoveスクリプトで定義しているSetOtherEvent関数を呼びます。

両手ともBlockに触っていないと判断された時は、UnsetPushEvent関数を呼び出して、ロックを解除します。

OnControllerColliderHit関数はCharacterControllerコンポーネントを設定しているオブジェクトが他のオブジェクトと接触した時に呼ばれる関数です。

飛ばしたレイがヒットしたコライダのゲームオブジェクトのタグがBlockだった時はそのブロックのRigidbodyに力を加えて押します。

OnAnimatorIKはアニメーターコントローラでレイヤーのIK Passのチェックを入れていると呼ばれます。

右手と左手それぞれからのレイがBlockに当たっていたら、
IKウエイトを1にして当たった位置と角度に右手、左手をそれぞれ設定します。

これでPushOtherスクリプトが完成しました。
次に右肩、左肩からレイを出す位置RightHandRayとLeftHandRayを作成します。

キャリー1

上のようにキャラクターの子要素に設定します。

キャリー2

Debug.DrawRayで見える化したレイが上のようになります。
このレイがBlockレイヤーに設定した物に当たった場合、そこの位置と角度に合わせて、右手と左手をもっていきます。

キャリー3

押す物のサンプルとしてCubeを作り、インスペクタで上のようにRigidbodyを追加します。

RigidbodyのInterpolateはExtrapolate、Collision DetectionはContinuousにします。
InterpolateのExtrapolateは補間を次フレームを予測して行います。

Collision Detectionは連続的な衝突検知の方法でBlockに主人公がめり込まないようにする為
Continuousにしました。Continuousは連続した検知をするのに適しています。

主人公操作スクリプトMoveの修正

最後に主人公操作スクリプトMove内を修正します。

上の方の条件文はMoveスクリプトのUpdate関数内の記述で、近接攻撃や射撃の構え等へ移行する処理です。
そこに!pushEventを加え、pushEventがOnの時はそれらの攻撃への移行をしないようにします。

pushEventのOn、OffはSetPushEventとUnsetPushEventで行っています。
これでスクリプトの設定が完了しました。
Unityの実行ボタンを押して確認してみましょう。

キャリー4

上のようにBlockレイヤーに指定された物を押す事が出来るようになりました。

キャリー5

上のようにレイが外れている左手は物を触りません。

これでIKを使って物を押す事が出来るようになりました。
主人公の移動スピードそのままなのと、走っているアニメーションをそのまま使っているので、
ダンボールのような軽い物を押している感じになっています。
押している状態になったら、アニメーションのスピードを落としたり、アニメーション自体を別に作成したり、といった対処が必要になるかと思います。