[Unity3D] 攻撃の実装②

死亡アニメーションの実装
---
1, CreateState→Emptyで作成し、nameをDeathに変更する
![6744308E-E1BC-4825-8193-FF280FE52930_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/9e7ced78-c9bd-9f4c-89be-30afdd592879.jpeg)

2, Triggerでdieを作成し、AnyStateからDeathにMakeTrabsitionを作成。MotionにDeathアニメーションをつける
![BDDFEA96-5B98-4202-8674-D00D4777B777_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/ae42069d-9053-94af-c581-9ae99055dd56.jpeg)

3, Conditionsにdieを設定する
![793A2B7E-D709-4BCC-8C3E-F7538505D3F2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/3878d72d-da12-7fbb-4c62-e4aaaba50791.jpeg)

4, Health.csに下記スクリプトを追加

```C#:Health.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Combat
{
    public class Health : MonoBehaviour
    {
        [SerializeField] float healthPoints = 100f;

        bool isDead = false;    

        public void TakeDamage(float damage)
        {
            healthPoints = Mathf.Max(healthPoints - damage, 0);
            if (healthPoints == 0) 
            {
                Die();
            }
        }
           
        private void Die()
        {
            if (isDead) return;

            isDead = true;
            GetComponent<Animator>().SetTrigger("die");
        }
    }
}
```

5, EnemyのCapsuleColliderを合わせ、ApplyAllする
![B2FE1CD6-0D20-402E-8E47-C9AD808E0DED.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/7b98e222-15d2-8c2e-6c59-fadafafb6399.jpeg)
![B580DDA0-76B2-4BE2-870E-92B2DD615D5C_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/5588acb0-61dd-dbaf-dc52-55d25f00a632.jpeg)

6, AttackからLocomotionにMakeTransitionを返し、一つ目のLocomotionのHaExitTimeのチェックを外す。ParameterでstopAttackを作成し、Conditionsに設定する。
![75C2969B-7559-441E-9E0D-5DF061F278CB.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/940521/10d10077-4c67-76b1-4afe-69cbd9eafd54.jpeg)

7,  下記スクリプトを追加する

```C#:Health
sing System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Combat
{
    public class Health : MonoBehaviour
    {
        [SerializeField] float healthPoints = 100f;

        bool isDead = false;

        public bool IsDead()    //追加
        {
            return isDead;
        }

        public void TakeDamage(float damage)
        {
            healthPoints = Mathf.Max(healthPoints - damage, 0);
            if (healthPoints == 0) 
            {
                Die();
            }
        }
           
        private void Die()
        {
            if (isDead) return;

            isDead = true;
            GetComponent<Animator>().SetTrigger("die");
        }
    }
}
```

```C#:Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float weaponRange = 2f;
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] float weaponDamage = 5f;

        Health target;  //変更
        float timeSinceLastAttack = 0;

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;    //追加

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position);    //追加
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        private void AttackBehaviour()
        {
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                GetComponent<Animator>().SetTrigger("attack");
                timeSinceLastAttack = 0;
            }
        }

        void Hit()
        {
            target.TakeDamage(weaponDamage);    //変更
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < weaponRange;   //追加
        }

        public void Attack(CombatTarget combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();   //追加
        }

        public void Cancel()
        {
            GetComponent<Animator>().SetTrigger("stopAttack");  //追加
            target = null;
        }
    }
}
```

```C#:Combat
using UnityEngine;
using System.Collections;

namespace RPG.Combat
{
    [RequireComponent(typeof(Health))]

    public class CombatTarget : MonoBehaviour
    {

    }
}
```

8, 攻撃時に相手の方を向く。 下記のスクリプトを追加

```C#:Fighter
private void AttackBehaviour()
        {
            transform.LookAt(target.transform); //攻撃時にターゲットの方を向く
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                GetComponent<Animator>().SetTrigger("attack");
                timeSinceLastAttack = 0;
            }
        }
```

死亡した敵を無視する
---
1, 下記スクリプトの追加

```C#:PlayerController
private bool InteractWithCombat()
        {
            RaycastHit[] hits = Physics.RaycastAll(GetMouseRay());
            foreach (RaycastHit hit in hits)
            {
                CombatTarget target = hit.transform.GetComponent<CombatTarget>();

                //追加
                if (!GetComponent<Fighter>().CanAttack(target))
                {
                    continue;
                }

                if (Input.GetMouseButtonDown(0))
                {
                    GetComponent<Fighter>().Attack(target);
                }
                return true;
            }
            return false;
        }
```

```C#:Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float weaponRange = 2f;
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] float weaponDamage = 5f;

        Health target;
        float timeSinceLastAttack = 0;

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();    //追加
                timeSinceLastAttack = 0;
            }
        }

        //追加
        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }  //追加
            target.TakeDamage(weaponDamage);
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < weaponRange;   //追加
        }

        //追加
        public bool CanAttack(CombatTarget combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(CombatTarget combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();   //追加
            target = null;
        }

        //追加
        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}
```