キャラクターがドアを通るときに、頭や腕が壁を突き抜けることがあります。
Animation Riggingパッケージを使って、ドアを通過するときのアニメーションを動的に編集してみました。
Rigを設定
Animation Riggingでキャラを自然に振り向かせるとは別に空のゲームオブジェクトを作って、Rigコンポーネントを追加します。
キャラクターのルートに追加したRig Builderコンポーネントにアタッチします。
Chain IK Constraint
まずはRigの子として空のゲームオブジェクトを作り、頭と腕のためにChain IK Constraintコンポーネントを付けてみます。
Tipに頭、Rootに脊椎のボーンをアタッチします。このゲームオブジェクトのさらに子としてターゲット用の空のゲームオブジェクトを作り、Targetにアタッチしています。
ターゲットの位置を調節
ターゲットをプレイヤーの顔の前に置きました。
プレイモードでターゲットを動かすと頭から腰までのボーンが付いてきます。
左右にも動かせます。
腕も同様に、体の内側へ動かしました。
トリガー
ドアを通るときだけ、RigのWeightを上げて、ドアを抜けたらWeightを0まで下げます。ドアの前にBoxコライダーのトリガーを置き、敵のスクリプトのOnTriggerEnterとOnTriggerExitでWeightの変更を開始します。
- [SerializeField] Rig doorRig;
- [SerializeField] float doorRigMaxWeight = 0.538f;
-
- IEnumerator IncreaseDoorRig()
- {
- while (doorRig.weight < doorRigMaxWeight)
- {
- doorRig.weight += Time.deltaTime;
-
- yield return null;
- }
-
- doorRig.weight = doorRigMaxWeight;
- }
-
- IEnumerator DecreaseDoorRig()
- {
- while (doorRig.weight > 0f)
- {
- doorRig.weight -= Time.deltaTime * 0.8f;
-
- yield return null;
- }
-
- doorRig.weight = 0f;
- }
RigやコンストレイントのWeight、ターゲットの位置などで動きを調節できます。
Two Bone IK Constraint
今度は、Two Bone IK Constraintを使ってキャラクターの左手をドア枠に触れさせてみます。同じRigの子として空のゲームオブジェクトを作り「Two Bone IK Constraint」コンポーネントを追加しました。
Tipに左手のボーンをアタッチします。
コンテキストメニューを開いて、「Auto Setup from Tip Transform」をクリックします。
すると、左腕のボーンがアタッチされました。また、コンストレイントの子として、ターゲットとHintのゲームオブジェクトが作られて自動でアタッチされます。
ドア枠の位置に固定するために、ターゲットにはParent Constraintコンポーネントを付けました。ソースはスクリプトで設定します。
元々の左腕のコンストレイントはウェイトを0にしておきます。
ターゲットとヒントを調節
プレイモードでターゲットとヒントの位置や回転を調節しました。ヒントの位置で肘の向きを変えられます。
ターゲットの回転で手の向きを変えられます。
手を置く位置を設定
調節したターゲットの位置に空のゲームオブジェクトを起きます。これはドアのトリガーの子にしました。
トリガーにはスクリプトを付け、手を置くTransformを公開しておきます。
- using UnityEngine;
-
- public class EnemyDoorTrigger : MonoBehaviour
- {
- [SerializeField] Transform armIKTarget;
- public Transform ArmIKTarget => armIKTarget;
- }
キャラクターのスクリプトでは、トリガーにEnter/Exitした時やウェイトが0になったときに、コンストレイントの有効無効を切り替えています。
- IEnumerator DecreaseDoorRig()
- {
- while (doorRig.weight > 0f)
- {
- doorRig.weight -= Time.deltaTime * 0.8f;
-
- yield return null;
- }
-
- doorRig.weight = 0f;
-
- DisableHandPlacementOnDoorframe();
- }
- protected override void OnTriggerExit(Collider other)
- {
- base.OnTriggerExit(other);
-
- if (other.tag == "DoorTrigger")
- {
-
- //DisableHandPlacementOnDoorframe();
- StopCoroutine("IncreaseFoorRig");
- StopCoroutine("DecreaseFoorRig");
- StartCoroutine("DecreaseFoorRig");
- }
- }
-
- [SerializeField] ParentConstraint leftArmTarget;
-
- void EnableHandPlacementOnDoorframe(Transform target)
- {
- // ソースを作成
- var source = new ConstraintSource();
- source.sourceTransform = target;
- source.weight = 1f;
-
- // 左腕のParentコンストレイントにソースを追加
- leftArmTarget.AddSource(source);
-
- // コンストレイントを有効にする。
- leftArmTarget.constraintActive = true;
- }
-
- void DisableHandPlacementOnDoorframe()
- {
- // ソースを削除
- leftArmTarget.RemoveSource(0);
-
- // コンストレイントを無効にする
- leftArmTarget.constraintActive = false;
-
- }