Unity: IKを利用して両手持ち武器の左手の位置を合わせる

Unity入門 - Project Bonfire

武器を両手で持った際に左手の位置があっていなかったので、今回はそれを合うようにします。

IKって難しそうな気がしていたのですが、やってみたら大したことなかったです。今回の内容は Unity のマニュアルほとんどそのままです。

IK - Unity マニュアル
ほとんどのアニメーションは、スケルトンのジョイントを回転させ事前に決められた値に変更して作られます。子ジョイントの位置は、親の回転にもとづいて変化します。このた...

実装

Animator の設定

はじめに IKを使えるようにするために、Animator にIK Passの設定を行います。

Animator タブで、Layers を選択、Base Layer の歯車マークを押して出てきたウィンドウの IK Pass にチェックを入れます。

左手の位置情報を設定する

以下のように R_HandWeapon の下で Create Empty を実行して、空のゲームオブジェクトを作成し、HandPosition という名称にします。WeaponMaul との順番に気を付けてください。順番が違うと武器の着脱に影響が出ます。

最初、左手の位置情報を武器側に持たせよと思ったのですが、武器をInstanciate/Destory する際に位置情報の設定し直しが面倒な気がしたので、左手の位置情報は手の方に付けました。

試していませんが、こうしておくと別の武器でもそのまま位置情報が使えると思います。

スクリプトの取り付け

IKControl.cs スクリプトを取り付けてます。取り付けるのは Knight プレハブ直下です。そして、HandPosition を LeftHandObjに設定しておきます。

位置合わせ

気合で位置と角度を合わせます。今更ですが、Global と Local を使い分けると便利なことに気が付きました。

やりかたは、武器の取り付けをやった時と同じです。

  • 実行状態で一時停止させる
  • Scene 画面で HandPositionの 位置調整をする
  • 実行状態のまま、HandPosition のInspector に行き、Transform で右クリックして CopyComponent を実行する
  • 実行を止める
  • HandPosition のTransform で右クリックして Paste Component Values を実行する

これで再度実行して、位置が合っていれば OK です。

忘れずにプレハブを Override しておきます。

位置合わせ前

位置合わせ後

位置合わせ後、別角度

若干指が埋もれていますが、このくらいが私の限界です。

スクリプト

追加したスクリプトは IK Control.cs です。それと Weapon.cs に変更を加えています。

IKControl.cs

ほぼ、Unity のマニュアル通りなので説明の必要はないと思います。

using UnityEngine;

namespace Bonfire
{
    public class IKControl : MonoBehaviour
    {
        public Transform leftHandObj = null;

        Animator animator;
        internal bool isIKActive = true;

        void Start()
        {
            animator = GetComponent<Animator>();
        }

        // IK を計算するためのコールバック
        void OnAnimatorIK()
        {
            if (animator)
            {
                if(isIKActive){
                    if (leftHandObj != null)
                    {
                        // 指定されている場合は、左手のターゲット位置と回転を設定する
                        animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);
                        animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
                        animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandObj.position);
                        animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandObj.rotation);
                    }

                }
                else
                {
                    //IK が有効でなければ、手の位置と回転を元の位置に戻す
                    animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 0);
                    animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 0);
                }
            }
        }
    }

}

Weapon.cs

R_HandWeapon の下に HandPosition を作成したので、武器の持ち替えの際に削除する武器を GetChild するときの引数を調整しました。

        private void DestroyOldWeapon()
        {
            if (weaponHandPosition.childCount > 1)
            {
                Transform oldWeapon = weaponHandPosition.GetChild(1);
                if (oldWeapon != null)
                {
                    oldWeapon.parent = null;
                    Destroy(oldWeapon.gameObject);
                }
            }

            if (weaponBackPosition.childCount > 0)
            {
                Transform oldBackWeapon = weaponBackPosition.GetChild(0);
                if (oldBackWeapon != null)
                {
                    Destroy(oldBackWeapon.gameObject);
                }
            }
        }

ファイル

おわりに

これで、両手で武器を持つようになったと思います。これまでは、両手武器と言いつつ実は片手で振ってました。