My attack state for my fsm isn't working

Hey guys please check my fsm here my attack state doesn’t seem to work and idk why. If anyone knows please please help thank you.

public class PlagueFiniteStateMachine : MonoBehaviour
{
    
    public enum doctorState
    {
        // define every state needed
        Walking,
        Attack,
        Charge,
        Return,
        Idle,
        Follow,

    }

    [SerializeField] float visibleDis= 15f;
    [SerializeField] float visibleAngle= 10f;
    [SerializeField] int attack= 10;
    [SerializeField] float distance = 5f;


    private Animator ani;
    public LayerMask ignore;


    [SerializeField] private GameObject player;
    [SerializeField] NavMeshAgent plague;
    [SerializeField] GameObject targetPos;
    [SerializeField] GameObject aiBase;
    [SerializeField] GameObject vaccine;


    [SerializeField]GameObject Indicator;
    PlayerMovement op;


    //[SerializeField]Vector3 destination;

    doctorState currentState;

    
    void Start()
    {
        Indicator.SetActive(false);

        plague = GetComponent<NavMeshAgent>();
        ani = this.GetComponent<Animator>();
        op = GetComponent<PlayerMovement>();

        player = GameObject.FindGameObjectWithTag("Player");

        targetPos = GameObject.FindGameObjectWithTag("Flag");
        aiBase = GameObject.FindGameObjectWithTag("AIBase");
        vaccine = GameObject.FindGameObjectWithTag("Flag");

        ChangeState(doctorState.Walking); // on entry walking state is default state
        plague.SetDestination(targetPos.transform.position);

    }

   
    void Update()
    {
        float stillAttack = Vector3.Distance(player.transform.position, plague.transform.position);

        StateHandler();
        if (stillAttack <= distance)
        {
            ChangeState(doctorState.Attack);

            plague.SetDestination(player.transform.position);
            op.takeDamage(attack);
        }

    }

    // method to handle behaviours between states & conditions to switch states 
    void StateHandler()
    {
        switch (currentState)
        {
          case doctorState.Walking:
                if (CanSeePlayer())
                {
                    ChangeState(doctorState.Charge);
                }
                else if (CanAttack())
                {
                    ChangeState(doctorState.Attack);
                }
                else if (ReachedTarget())
                {
                    ChangeState(doctorState.Return);
                }
                break;

            case doctorState.Attack:
                if(CanSeePlayer())
                {
                    ChangeState(doctorState.Charge);
                }
                else if (!CanAttack())
                {
                    ChangeState(doctorState.Walking);
                }
                else if(ReachedTarget())
                {
                    ChangeState(doctorState.Return);
                }                
                break;
            
               case doctorState.Charge:
                if (!CanSeePlayer())
                {
                    ChangeState(doctorState.Walking);
                }
                else if(CanAttack())
                {
                    ChangeState(doctorState.Attack);
                }
                else if (ReachedTarget())
                {
                    ChangeState(doctorState.Return);
                }
                break;

                case doctorState.Return:
                if (AgentReturnedVaccine()) 
                {
                    Debug.Log("Agent secured the package, We have failed.");
                    ChangeState(doctorState.Idle);
                }
                break;
  
        }
                 
    }

    // method to handle the behaviour in transition between states
    void ChangeState(doctorState newState)
    { 
        if(currentState== newState) return; // to prevent unnecessary transitions;

        currentState= newState;

        //here is the enter logic for current states

        switch (currentState)
        {
            case doctorState.Walking:
                OnEnterWalking();
                break;

              case doctorState.Charge:
                OnEnterCharge();
                break;

               case doctorState.Attack:
                OnEnterAttack();
                break;

                case doctorState.Idle:
                  OnEnterIdle();
                  return;

                 case doctorState.Return:
                  OnEnterReturn();
                  break;
        }    

       
    }

    // Enter and exit methods for each state
    void OnEnterWalking()
    {
        ani.SetBool("CanAttack", false);
        ani.SetBool("CanCharge", false);
        ani.SetBool("IsWalking", true);
       
        plague.SetDestination(targetPos.transform.position);
       
        Debug.Log("Current state is:  " + currentState);

    }
    
    void OnEnterIdle()
    {
        Indicator.SetActive(false);

        ani.SetBool("CanAttack", false);
        ani.SetBool("CanCharge", false);
        ani.SetBool("IsWalking", false);
        ani.SetBool("IsIdle", true);

       

        Debug.Log("Current state is:  " + currentState);

    }

    void OnEnterCharge()
    {
        Indicator.SetActive(false);

        ani.SetBool("IsWalking", false);
        ani.SetBool("CanAttack", false);
        ani.SetBool("CanCharge", true);
       
        plague.SetDestination(player.transform.position);

        Debug.Log("Current state is:  " + currentState);
    }

    void OnEnterAttack()
    {
        Indicator.SetActive(false);
       // float stillAttack = Vector3.Distance(player.transform.position, plague.transform.position);

        ani.SetBool("CanCharge", false);
        ani.SetBool("IsWalking", false);
        ani.SetBool("CanAttack", true);
        /*
        if(stillAttack<= distance)
        {
            plague.SetDestination(player.transform.position);
            op.takeDamage(attack);
        }
       */


        Debug.Log("Current state is:  " + currentState);
    }

    void OnEnterReturn()
    {
        Indicator.SetActive(true);

        ani.SetBool("IsWalking", false);
        ani.SetBool("CanAttack", false);
        ani.SetBool("CanCharge", true);

        plague.SetDestination(aiBase.transform.position);
        Debug.Log("Current state is:  " + currentState);

    }

    bool Captured(Collider other)
    {
        string thisTag = "Agent";
        GameObject someObject = GameObject.FindGameObjectWithTag(thisTag);
        if (other.gameObject.tag == "flag" && someObject != null)
        {
            return true;
        }
        return false;
    }



    public bool CanSeePlayer() // boolean method to check if player is in range
    {
       Vector3 direction=  player.transform.position- this.transform.position;
         float angle= Vector3.Angle(direction, this.transform.forward);

        if(direction.magnitude< visibleDis && angle< visibleAngle)
        {
            return true;
        }
        return false;
    }

    public bool CanAttack()
    {
        /*
         RaycastHit hit;
         int maxDis = 5;
        ;

         if(Physics.Raycast(transform.position, Vector3.forward, maxDis,  ignore ))
         {
             Debug.DrawRay(transform.position, transform.forward, Color.red);
             Debug.Log("Hit " + player);
             return true;
         }
         return false;
        */

        if(Vector3.Distance(player.transform.position,plague.transform.position) <= distance)
        {
            return true;
        }
        return false;
        
       
        
    }

    bool ReachedTarget()
    {
        if(Vector3.Distance(targetPos.transform.position, plague.transform.position) < distance)
        {
            return true;
        }
        return false;
    }

   
    bool AgentReturnedVaccine()
    {
        return Vector3.Distance(aiBase.transform.position, plague.transform.position) < distance;
    }

    void OnTriggerEnter(Collider other)
    {
        if (Captured(other))
        {
            Debug.Log("Returning to base");
            ChangeState(doctorState.Return);
        }
    }

  
}

4 Answers

4

Well, your “visibleDis” is larger than your “distance” (you should choose more descriptive names here). if-else chains will only execute “one” if statement and they are evaluated in order. So if the first one is true, none of the following would even be evaluated. Since your “CanSeePlayer” condition is true with an even larger distance than your “CanAttack” condition, your CanAttack if statement would not be evaluated unless CanSeePlayer is false. For this to be true you would need to “look away” from the target.

You are very inconsistent with the object references you’re using which makes it difficult to follow your code. For example your CanSeePlayer uses this.transform to evaluate the distance an angle while your CanAttack method uses plague.transform. However they are both the same since plague is just a component on the same gameobject. You should avoid such arbitrary references. When you come back to your own code in a year this makes the code just much harder to read.

Anyways, you should think about the order of evaluation of your code. Just having a condition somewhere does not guarantee that it will be reached / checked.

I haven’t checked all your code and logic as it’s a quite long and convoluted construct.

Also keep in mind that your StateHandler is called every frame. Your attack state would jump back to Charge and Charge would jump to Attack. You really need to think again about your transitions between states.

Thank you for the advice

And since my statehandler is in the update function isn’t that the reason for my if statements?

I think I see a few inconsistencies in the StateHandler. I’m making some assumptions on the functionality you’re wanting, but maybe try this in replace of your StateHandler method and see how it behaves

void StateHandler()
{
    switch (currentState)
    {
      case doctorState.Walking:
            if (CanSeePlayer())
            {
                ChangeState(doctorState.Charge);
            }
            else if (ReachedTarget())
            {
                ChangeState(doctorState.Return);
            }
            break;
        
           case doctorState.Charge:
            if (!CanSeePlayer())
            {
                ChangeState(doctorState.Walking);
            }
            else if(CanAttack())
            {
                ChangeState(doctorState.Attack);
            }
            else if (ReachedTarget())
            {
                ChangeState(doctorState.Return);
            }
            break;

case doctorState.Attack:
            if(!CanSeePlayer() || !CanAttack())
            {
                ChangeState(doctorState.Walking);
            }
            else if(ReachedTarget())
            {
                ChangeState(doctorState.Return);
            }                
            break;

            case doctorState.Return:
            if (AgentReturnedVaccine()) 
            {
                Debug.Log("Agent secured the package, We have failed.");
                ChangeState(doctorState.Idle);
            }
            break;
  
    }
             
}