Unity: 武器の着脱と、AnimatorOverrideController を使ったアニメーションの変更

Unity入門 - Project Bonfire

今回は武器を装備したり外したりできるようにします。また、武器を装備しているときと、装備を外しているときで別のアニメーションになるようにします。

アニメーション周りの実装

パラメータの追加

装備を付けたり外したりするアニメーションを実行するためのフラグを追加します。Bool 型で EquiptWeapon と UnequipWeapon を追加しました。

Animator の設定

アニメーションを追加します。2Hand-Sword-Unsheath-Back-Unarmed を使うのですが、名称が長いので Equip-Weapon としました。

同様に 2Hand-Sword-Unsheath-Back-Unarmed を Unequip-Weapon として追加します。

それぞれの Transition は次のようにしています。Locomotion から Equip-Weapon へは、Has Exit Time のチェックを外して、Conditions に EquipWeapon = true を入れます。タイミングはあまりシビアでなくていいので、左にずらすことはしませんでした。このあたりは、お好みで。

Equip-Weapon から Locomotion への戻りは、Has Exit Time で戻ります。

Unequip-Weapon は、Equip-Weapon と同様に設定します。

アニメーションの確認

今まで作成した攻撃やローリングのアニメーション同様に、アニメーションが終了するタイミングで関数呼び出しを追加するのですが、最初から WeaponSwitch というのが付いていますので、これを使用することにします。

Animator Override Controller の作成

装備を外したときのアニメーションを追加するために、Animator Override Controller を使用します。元の Animator Controller に対して、変更したいアニメーションを指定することができます。

さっそく追加します。

名所を UnarmedAnimatorOverrideController としました。

Inspector に移動します。

まず、Controller にもとになる Animator Controller を設定します。今回は、プレイヤーキャラクターに設定してある PlayerAnimatorController です。

Controller を設定したら、差し替えるアニメーションを設定します。2Hand-Sword-Sheath-Back-Unarmed と 2Hand-Sword-Unsheath-Back-Unarmed、あとBasicMotions のところは None のままにします。Attack1, Attack4 などは好きなモーションを選んでください。

アニメーションの設定

以前、攻撃やローリングを作成したときと同様に、アニメーションの長さの調整やイベントの設定を行います。

以下のように Project 画面から直接アニメーションを選択して設定していきます。

Unarmed-Roll-Forward は、ローリング後の待機時間が長いため、時間を短くしました。それと、終了間際にCallRollingEnd を呼ぶようにイベントを設定します。

Unarmed-Attack-R2は、実行時間は問題なかったっためイベントだけ設定します。以下のように、StartAttackHit と EndAttackHit と CallAttackR1End を 設定します。

Unarmed-Attack-L3 も同様です。こちらは、終了時のイベントで呼び出すのは CallAttackR1_2End です。

パンチの作成

武器の1種としてパンチを作成します。

R_HandWeapon の下で Create Empty を実行して空の GameObject を作成します。そしてそれをWeaponPunch という名称にします。

WeaponPunch に以下の設定を行います

  • Layer を PlayerAttack にする
  • SphereCollider を取り付ける
  • SphereCollider の IsTrigger にチェックを入れる
  • SphereCollider 位置とサイズを設定する。これはお好みで。この設定は少し大きめにしてあります。
  • AttackArea.cs を取り付ける

ここまで設定したらプレハブ化します。以下のように PlayerCharacter の下に配置しました。プレハブ化が終わったらKnight の R_HandWeapon から WeaponPunch は削除してください。

プレハブ内を見てみるとSphere Collider しかない状態です。

スクリプトの取り付けとプレハブの設定

まず、Weapon.cs を Knight に取り付けます。

Weapon (Script) の Animator Override ~ WeaponHandPunchPrefab をそれぞれ画像のように設定してください。

スクリプトの差し替え

スクリプトを今回添付したものに差し替えてください。

ファイルは記事の末尾に添付してあります。

以上で、武器の付け外しと攻撃アニメーションの変更ができ、パンチで攻撃するとパンチでも敵にダメージが入るはずです。パンチは右だけにしか攻撃判定を付けていないため、左での攻撃では敵にダメージが入りません。

左の攻撃もダメージが入るようにするためにはもうひと手間必要なので、今回は、つけませんでした。2段目の攻撃も右手にしてしまえば、2段目の攻撃でもダメージが入るようになります。

武器の装備を付けたり外したりするのは、十字キーの右ボタンです。

スクリプトの説明

スクリプトの簡単な説明です。

PlayerStatus.cs

変数に、EquipWeapon と UnequipWeapon を追加しています。

        //状態
        public bool isGrounded = false;
        public bool isAttackR1 = false;
        public bool isAttackR1_2 = false;
        public bool isRolling = false;
        public bool isJumping = false;
        public bool isDied = false;
        public bool EquipWepon = false;
        public bool UnequipWeapon = false;

PlayerAnimation.cs

まず、今回作成した Wepon クラスを呼び出せるようにします。

       Weapon weapon = null;
<中略>
        void Start()
        {
            animator = GetComponent<Animator>();
            status = GetComponent<PlayerStatus>();
            weapon = GetComponent<Weapon>();
        }

EquipWeapon と UnequipWeapon の終了処理である WeaponSwitch() を実装します。フラグをリセットする前に、武器の装備処理 (※後述) を呼び出して、武器の着脱を行います。

        void WeaponSwitch()
        {
            if(status.EquipWepon)
            {
                weapon.WeponEquipment(true);
            }
            if(status.UnequipWeapon)
            {
                weapon.WeponEquipment(false);
            }
            status.resetStatus();
        }

あとは、EquipWeapon と UnequipWeapon の設定をする処理を追加しています。

        void Update()
        {
            animator.SetBool("IsGrounded", status.isGrounded);
            animator.SetBool("IsAttackR1", status.isAttackR1);
            animator.SetBool("IsAttackR1_2", status.isAttackR1_2);
            animator.SetBool("IsRolling", status.isRolling);
            animator.SetBool("IsJumping", status.isJumping);
            animator.SetBool("EquipWeapon", status.EquipWepon);
            animator.SetBool("UnequipWeapon", status.UnequipWeapon);
        }

PlayerCtrl.cs

十字キーの入力 と装備の切り替え

十字キーは、Axis として認識されます。実際に来る値は 0 と 1のはずです。以下のように、canInputDpadRightをフラグにして、1が来たら次は0がくるまで入力を受け取らないようにしました。

十字キーを使って武器を装備しているかどうかのフラグを切り替え、次の状態遷移を決めます。

        // 十字キーの入力用フラグ
        bool canInputDpadRight = true;

<中略>
        // 武器を装備しているかどうかのフラグ
        bool isEquipWeapon = true;
<中略>

            if (canInputDpadRight && Mathf.Approximately(dPad, 1f))
            {
                if (isEquipWeapon)
                {
                    nextState = State.UnEquipWeapon;
                }
                else
                {
                    nextState = State.EquipWeapon;
                }
                isEquipWeapon = isEquipWeapon ? false : true;
                canInputDpadRight = false;
            }
            if (Mathf.Approximately(dPad, 0f))
            {
                canInputDpadRight = true;
            }

State とそれに関連するメソッドの追加

武器装備と装備解除の State を追加します。

    public enum State
    {
        Died,
        Walking,
        AttackR1,
        AttackR1_2,
        Rolling,
        Jump,
        EquipWeapon,
        UnEquipWeapon
    };

これに関連して Switch に、StartEquipWeapon(), StartUnEquipWeapon() の処理を追加し、それぞれの関数を作成しました。

            // 次の状態に遷移できるようになった時の処理
            if (isStateEnd)
            {
                State prevState = state;
                // StartXX 内で nextState が書き換えられるので、ここで state を更新しておく必要がある
                state = nextState;
                switch (nextState)
                {
                    case State.Walking:
                        StartWalking(prevState);
                        break;
<中略>
                    case State.EquipWeapon:
                        StartEquipWeapon(prevState);
                        break;
                    case State.UnEquipWeapon:
                        StartUnEquipWeapon(prevState);
                        break;

                }
            }
        private void StartEquipWeapon(State prevState)
        {
            StateStartCommon();
            status.EquipWepon = true;
        }

        private void StartUnEquipWeapon(State prevState)
        {
            StateStartCommon();
            status.UnequipWeapon = true;
        }

Weapon.cs

新規に追加したクラスです。武器関連の処理を扱うクラスです。

変数の定義

AnimatorOverrideController と武器のプレハブ、武器の位置を以下のような変数で持ちます。

        // 武器の装備関連
        [SerializeField] AnimatorOverrideController animatorOverride = null;
        [SerializeField] GameObject weaponBackPrefab = null;
        [SerializeField] GameObject weaponHandMaulPrefab = null;
        [SerializeField] GameObject weaponHandPunchPrefab = null;
        Transform weaponBackPosition = null;
        Transform weaponHandPosition = null;

武器を取り付ける場所を取得

Start() 時に、武器を持つ位置を取得します。背中の位置と右手の位置をそれぞれ名前で検索します。

        void Start()
        {
            animator = GetComponent<Animator>();
            status = GetComponent<PlayerStatus>();
            playerMove = GetComponent<PlayerMove>();
            hudController = FindObjectOfType<HudController>();

            weaponBackPosition = findTransformInChildren(transform, "BackWeaponPosition");
            weaponHandPosition = findTransformInChildren(transform, "R_HandWeapon");
        }

        // name で指定した オブジェクトを探すメソッド
        Transform findTransformInChildren(Transform tf, string name)
        {
            Transform ret = null;

            if (tf.name == name)
            {
                return tf;
            }
            for (int i = 0; i < tf.childCount; i++)
            {
                ret = findTransformInChildren(tf.GetChild(i), name);
                if (ret != null)
                {
                    return ret;
                }
            }

            return null;
        }

武器の廃棄

武器の廃棄メソッドは次のようにしています。武器の付け替えの際は、背中についているものと右手に持っているものを両方 Destroy するように書いてあります。実際には、どちらか一方にしか武器は付いていないはずですので、Destroy が呼ばれるのは1回です。

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

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

武器の装備

コメントに書いてある通りです。

        public void WeponEquipment(bool isArmed)
        {
            DestroyOldWeapon();

            // 武器を装備する
            if (isArmed && weaponHandMaulPrefab != null)
            {
                Instantiate(weaponHandMaulPrefab, weaponHandPosition);
            }

            // 武器を背中に付ける
            if (!isArmed && weaponBackPrefab != null)
            {
                Instantiate(weaponBackPrefab, weaponBackPosition);
            }
            // 拳に当たり判定を付ける
            if (!isArmed && weaponHandPunchPrefab != null)
            {
                Instantiate(weaponHandPunchPrefab, weaponHandPosition);
            }

            // 攻撃側の当たり判定の再設定
            // ※ここでやろうとしたが、Destroy や Instantiage の実際の処理は後で行われる
            // ようなので、AttackArea を生成した時に呼ぶように変更した。
            // attackAreaActivator.SetAttackArea();

            // アニメーションの差し替え
            var overrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
            if (!isArmed && animatorOverride != null)
            {
                animator.runtimeAnimatorController = animatorOverride;
            }
            else if (overrideController != null)
            {
                animator.runtimeAnimatorController = overrideController.runtimeAnimatorController;
            }
        }

ファイル

おわりに

武器の着脱とアニメーションの変更が思ったより簡単にできました。AnimatorOverrideController って便利ですね。

次回、x ボタンだけでダッシュとローリングとジャンプをできるようにします。