【Unity】Animation Riggingでドアを自然に通過させる

投稿者: | 2023-11-26

キャラクターがドアを通るときに、頭や腕が壁を突き抜けることがあります。

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;

    }

コメントを残す

メールアドレスが公開されることはありません。