Fixing enemy behavior state machine in Unity
I'm working on a state machine for my game's NPC.
The only thing I can't to get work properly is the hit sequence. The hits register. What I would like to happen is for the attack sequence (animation) to stop, play the hit animation sequence, & then go back to the chase/attack state using a coroutine.
Currently, the attack sequence seems to just loop through, ignoring the coroutine.
Here's the code attached to the NPC: using UnityEngine; using System.Collections; ///
/// State pattern enemy. /// Handles animation sequences & states /// [RequireComponent(typeof(AudioSource))] public class StatePatternEnemy : MonoBehaviour { public Transform[] wayPoints; public float searchTurnSpeed = 120f; public float searchDuration = 5f; public float sightRange = 15f; public float alertZone = 20f; public float stopAtDistance = 0f; //used to stop chase public float hitSequenceTimer = 1.25f; //use to return to attackState public float animWalkSpeed = 1f; public float animRunSpeed = 1.75f; public float animPunchSpeed = 1f; public float animGotHitSpeed = 1f;     public GameObject nmeFistL;
     public GameObject nmeFistR;
 
     public Vector3 offset = new Vector3(0,0.5f,0);//offsets raycast so it won't aim for foot
 
     public Transform eyes;
     public bool onAlert = false;
     public Transform chaseTarget;
 
     [HideInInspector]public float         playerDistance;  //player's transform info
     [HideInInspector]public float         alertSearchTimer;//increment timer search value
     [HideInInspector]public Vector3       enemyToTarget;   //player's Vector info
     [HideInInspector]public IEnemyState   currentState;
     [HideInInspector]public AlertState   _alertState;
     [HideInInspector]public AttackState  _attackState;
     [HideInInspector]public ChaseState   _chaseState;
     [HideInInspector]public HitState     _hitState;
     [HideInInspector]public PatrolState  _patrolState;
     [HideInInspector]public NavMeshAgent _navMeshAgent;
 
     public AudioClip sfxGrunterThreat;
     public float     sfxVolume = 0.125f;
 
     public AnimationClip nmeGotHit;
     public AnimationClip nmeIdle;
     public AnimationClip nmeJump;
     public AnimationClip nmePunch;
     public AnimationClip nmeRoar;
     public AnimationClip nmeRun;
     public AnimationClip nmeTaunt;
     public AnimationClip nmeWalk;
 
     AudioSource _sfx;
     RaycastHit  _hit;
 
     private void Awake()
     {
         _alertState  = new AlertState(this);
         _attackState = new AttackState(this);
         _chaseState  = new ChaseState(this);
         _hitState    = new HitState(this);
         _patrolState = new PatrolState(this);
 
         _navMeshAgent = GetComponent<NavMeshAgent>();
         _sfx          = GetComponent<AudioSource>();
     }
 
     // Use this for initialization
     void Start ()
     {
         currentState = _patrolState;
     }
     
     // Update is called once per frame
     void Update ()
     {
         currentState.UpdateState();
         Rayline();//remove later
         BeAlert();
         print(currentState);
     }
 
     private void OnTriggerEnter(Collider other)
     {
         if (other.gameObject.CompareTag ("fireBall"))
         {
             ///create a hit state to move player
             ///back & play the hit animation after coroutine
             currentState = _hitState;
             StopAnim();
             Debug.Log("fire");
             StartCoroutine(EnemyHitSequence());
         }
     }
     public void BeAlert()
     {
         ///ensure the player is within alert zone
         float playerDistance = Vector3.Distance(chaseTarget.position, transform.position);
         if(playerDistance > alertZone)
         {
             nmeFistL.SetActive(false);
             nmeFistR.SetActive(false);
             currentState = _patrolState;
             //print("Outside alertzone: " + playerDistance);
         }
         else
         {
             //print("Alertzone: " + playerDistance);
             //print("AlertZone Timer" + alertZoneTimer);
             onAlert = true;
             enemyToTarget = ((chaseTarget.position + offset) - eyes.transform.position);
 
             //check to see if player is in visual range & is hit with the rayCast
             if(Physics.Raycast(eyes.position, enemyToTarget, out _hit, sightRange) && _hit.collider.CompareTag("Player") && (playerDistance <= sightRange))
             {
                 currentState = _chaseState;  //player is found & being chased
                 StopChase();                 //player is being attacked
                 // _attackState setUp is in stopChase method
             }
             else// <---player not found but is close, be on alert!
             {
                 //currentState = _patrolState;
                 if(onAlert == true)
                 {
                     currentState = _alertState;            //look around for a bit
                     alertSearchTimer += Time.deltaTime;
                     ///if player isn't found, go patrol
                     if(alertSearchTimer >= searchDuration)
                     {
                         onAlert = false;
                         currentState = _patrolState;//nothing is found go back & patrol
                     }
                 }
             }
         }
     }
 
     public void StopChase()
     {
         _navMeshAgent.speed = 3.5f;//slow down the navAgent
         
         float playerDistance = Vector3.Distance(chaseTarget.position, transform.position);
         //stop navMeshAgent to begin attack sequence
         if (playerDistance <= stopAtDistance)
         {
             ///set this up to go on attack
             /// ensure enemy is looking at player
             enemyToTarget = ((chaseTarget.position + offset) - eyes.transform.position);
             if(Physics.Raycast(eyes.position, enemyToTarget, out _hit, sightRange) && _hit.collider.CompareTag("Player"))
             {
                 nmeFistL.SetActive(true);
                 _navMeshAgent.Stop();
                 currentState = _attackState;
             }
             ///ensure the enemy isn't hitting air
             /// & will always look for the player
             else
             {
                 transform.Rotate(chaseTarget.position, searchTurnSpeed * Time.deltaTime, 0);
             }
         }
         print("in stop chase");
     }
     
     public void EnemyGotHit()
     {
         //print("CurrentState in enemyGotHit: " + currentState);
         //transform.Translate(0f,0.25f,-0.125f);
         AnimGotHit();
         //_navMeshAgent.Stop();
         //StartCoroutine(EnemyHitSequence());
     }
 
     IEnumerator EnemyHitSequence()
     {
         EnemyGotHit();
         print("CurrentState in hit sqnz: " + currentState);
         yield return new WaitForSeconds(hitSequenceTimer);
         //_navMeshAgent.Resume();
         currentState = _attackState;
         StopChase();
         print("CurrentState after switch in coroutine: " + currentState);
     }
 
     public void Rayline()
     {
         Vector3 forward = transform.TransformDirection(Vector3.forward) * 10;
         Debug.DrawRay(eyes.transform.position, forward, Color.green);
     }
 
     public void StopAnim()
     {
         GetComponent<Animation>().Stop("nmeWalk");
         GetComponent<Animation>().Stop("nmePunch");
     }
 
     public void AnimWalk()
     {
         GetComponent<Animation>().Play("nmeWalk", PlayMode.StopAll);
         GetComponent<Animation>().wrapMode = WrapMode.Once;
         GetComponent<Animation>()["nmeWalk"].speed = animWalkSpeed;
     }
     public void AnimRun()
     {
         GetComponent<Animation>().Play("nmeRun", PlayMode.StopAll);
         GetComponent<Animation>().wrapMode = WrapMode.Once;
         GetComponent<Animation>()["nmeWalk"].speed = animRunSpeed;
     }
     public void AnimPunch()
     {
         GetComponent<Animation>().Play("nmePunch", PlayMode.StopAll);
         GetComponent<Animation>().wrapMode = WrapMode.Once;
         GetComponent<Animation>()["nmePunch"].speed = animPunchSpeed;
     }
     public void AnimRoar()
     {
         GetComponent<Animation>().Play("nmeRoar", PlayMode.StopAll);
         GetComponent<Animation>().wrapMode = WrapMode.Once;
         //GetComponent<Animation>()["nmeRoar"].speed = animRunSpeed;
     }
     public void AnimTaunt()
     {
         GetComponent<Animation>().Play("nmeTaunt", PlayMode.StopAll);
         GetComponent<Animation>().wrapMode = WrapMode.Once;
         GetComponent<Animation>()["nmeTaunt"].speed = animRunSpeed;
     }
     public void AnimGotHit()
     {
         GetComponent<Animation>().Play("nmeGotHit", PlayMode.StopAll);
         GetComponent<Animation>().wrapMode = WrapMode.Once;
         GetComponent<Animation>()["nmeGotHit"].speed = animGotHitSpeed;
     }
 
     public void SfxNmeThreat()
     {
         _sfx.PlayOneShot(sfxGrunterThreat, sfxVolume);
     }
 
 }
 Attack state:
 using UnityEngine;
 using System.Collections;
 /// <summary>
 /// Attack state stays within attack.
 /// Exiting out of attack sequence is handled within
 /// StatePatternEnemy
 /// </summary>
 public class AttackState : IEnemyState
 {
     private float searchTimer;
     private readonly StatePatternEnemy enemy;
     
     public AttackState (StatePatternEnemy _statePatternEnemy)
     {
         enemy = _statePatternEnemy;
     }
 
     public void UpdateState()
     {
         Look();
         AttackSequence();
     }
     
     public void OnTriggerEnter(Collider other)
     {
 
     }
     
     public void ToPatrolState()
     {
         enemy.currentState = enemy._attackState;
         searchTimer = 0f;
     }
     
     public void ToAlertState()
     {
         enemy.currentState = enemy._alertState;
         searchTimer = 0f;
     }
     
     public void ToChaseState()
     {
         enemy.currentState = enemy._attackState;
         searchTimer = 0f;
     }
     
     public void ToAttackState()
     {
         /// create a mono behavior for attack & other animation sequences
         /// check for distance
         /// if player is in range
         /// attack
         /// else
         /// ToChaseState();
         Debug.Log("Attacking");
     }
     public void ToHitState()
     {
         enemy.currentState = enemy._hitState;
         searchTimer = 0;
     }
     private void Look()
     {
         RaycastHit hit;
         Vector3 enemyToTarget = ((enemy.chaseTarget.position + enemy.offset) - enemy.eyes.transform.position);
         if(Physics.Raycast(enemy.eyes.position, enemyToTarget, out hit, enemy.sightRange) && hit.collider.CompareTag("Player"))
         {
             //Debug.Log("player found in: " + enemy.currentState);
             enemy.chaseTarget = hit.transform;
         }
         else//if player is hidden then
         {
             //Debug.Log("player not found in: " + enemy.currentState);
             ToAlertState();
         }
     }
     private void AttackSequence()
     {
         //RaycastHit hit;
         //Vector3 enemyToTarget = ((enemy.chaseTarget.position + enemy.offset) - enemy.eyes.transform.position);
         //if(Physics.Raycast(enemy.eyes.position, enemyToTarget, out hit, enemy.sightRange) && hit.collider.CompareTag("Player"))
         //{
         //    Debug.Log("start attack");
         //}
         //face the player
         //enemy.transform.Rotate(enemy.chaseTarget.position,enemy.searchTurnSpeed * Time.deltaTime, 0);//original code
         enemy._navMeshAgent.transform.LookAt(enemy.chaseTarget.position);
         enemy.AnimPunch();
     }
 }
 
               Finally, the hit state:
 using UnityEngine;
 using System.Collections;
 /// <summary>
 /// Attack state stays within attack.
 /// Exiting out of attack sequence is handled within
 /// StatePatternEnemy
 /// </summary>
 public class HitState : IEnemyState
 {
     private float searchTimer;
     private readonly StatePatternEnemy enemy;
     
     public HitState (StatePatternEnemy _statePatternEnemy)
     {
         enemy = _statePatternEnemy;
     }
     
     public void UpdateState()
     {
         Look();
         EnemyIsHit();
     }
     
     public void OnTriggerEnter(Collider other)
     {
         if (other.gameObject.CompareTag ("fireBall"))
         {
             Debug.Log("!");
             enemy.currentState = enemy._hitState;
 
         }
     }
     
     public void ToPatrolState()
     {
         enemy.currentState = enemy._patrolState;
         searchTimer = 0f;
     }
     
     public void ToAlertState()
     {
         enemy.currentState = enemy._alertState;
         searchTimer = 0f;
     }
     
     public void ToChaseState()
     {
         enemy.currentState = enemy._chaseState;
         searchTimer = 0f;
     }
     
     public void ToAttackState()
     {
         enemy.currentState = enemy._attackState;
         searchTimer = 0f;
     }
     public void ToHitState()
     {
 
     }
     private void Look()
     {
         RaycastHit hit;
         Vector3 enemyToTarget = ((enemy.chaseTarget.position + enemy.offset) - enemy.eyes.transform.position);
         if(Physics.Raycast(enemy.eyes.position, enemyToTarget, out hit, enemy.sightRange) && hit.collider.CompareTag("Player"))
         {
             enemy.chaseTarget = hit.transform;
         }
         else//if player is hidden then
         {
             ToAlertState();
         }
     }
     private void EnemyIsHit()
     {
         //Debug.Log("HitState.cs");
         //Debug.Log("EnemyIsHit() state: " + enemy.currentState);
         //enemy.currentState = enemy._attackState;
         //Debug.Log("EnemyIsHit() After state switch: " + enemy.currentState);
         enemy._navMeshAgent.transform.LookAt(enemy.chaseTarget.position);
         enemy.StopChase(); // calling StopChase method goes back to attackState
     }
 }
 
               I could really use some help fixing this, thank you
Your answer
 
             Follow this Question
Related Questions
Enemy AI script for 2D platformer 1 Answer
How to make an npc/ai walk next to the player? (C#) 1 Answer
Several NPC with waypoints 1 Answer
how to smoothly transition between animations in the "Any State" state machine. 1 Answer
Enemy watches player and shoots but bullets will not project.. 0 Answers