简体   繁体   中英

Unity AI attack Simple FSM Stackoverflow error

i have my zombies wandering, chasing me on LOS & FOV but when they reach me ( Camera Rig Vive VR ), only one attack me (not eveytime but he try). I would like to make them all to attack me once they are close to me ( the best would be they make a circle around me ).

If i try to change distance or use a trigger collider to pass a boolean to true instead of calulate the remaining distance (in the attack coroutine), i have stack overflow error. I dont understand why. If Someone can explane me, it would be very nice.

Here is code for AI_Enemy :

using UnityEngine;
using System.Collections;
using UltimateSpawner;
using System.Collections.Generic;

public class AI_Enemy : MonoBehaviour
{   
    public enum ENEMY_STATE {PATROL, CHASE, ATTACK, DEAD};

    public ENEMY_STATE CurrentState
    {
        get{return currentstate;}

        set
        {
            //Update current state
            currentstate = value;

            //Stop all running coroutines
            StopAllCoroutines();

            switch(currentstate)
            {
                case ENEMY_STATE.PATROL:
                    StartCoroutine(AIPatrol());
                break;

                case ENEMY_STATE.CHASE:
                    StartCoroutine(AIChase());
                break;

                case ENEMY_STATE.ATTACK:
                    StartCoroutine(AIAttack());
                break;

                case ENEMY_STATE.DEAD:
                    break;
            }
        }
    }
[SerializeField]
    private ENEMY_STATE currentstate = ENEMY_STATE.PATROL;

    [SerializeField] Animator ThisAnimator;
    [SerializeField] AudioSource ThisAudioSource;

    //Reference to patrol destination
    [SerializeField] GameObject[] PatrolDestinations;

    private AudioClip sound;
    public AudioClip[] attacksSounds;
    //Reference to line of sight component
    private LineSight ThisLineSight = null;

    //Reference to nav mesh agent
    private UnityEngine.AI.NavMeshAgent ThisAgent;

    //Reference to transform
    private Transform ThisTransform = null;

    //Reference to player health
    public PlayerHealth PlayerHealth = null;

    //Reference to player transform
    private Transform PlayerTransform = null;

    public Transform PatrolDestination;

    [SerializeField] float timeBetweenAttacks = 1.4f;

    private WaitForSeconds attackDelay;

    //Damage amount per second
    public float MaxDamage = 2.8f;

    public static bool inRange = false;




    void Awake()
    {
        ThisLineSight = GetComponent<LineSight>();
        ThisAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
        ThisTransform = GetComponent<Transform>();
        ThisAnimator = GetComponent<Animator>();
        ThisAudioSource = GetComponent<AudioSource>();

        attackDelay = new WaitForSeconds(timeBetweenAttacks);
    }

    void Start()
    {
        //Configure starting state
        ThisAgent.enabled = true;
        PlayerHealth = GameManager.Instance.Player;
        PlayerTransform = GameManager.Instance.EnemyTarget;
        PatrolDestinations = GameObject.FindGameObjectsWithTag("Waypoint");

        StartCoroutine(StartZombie());
    }

    public IEnumerator AIPatrol()
    {
        ThisAnimator.SetBool("Attack", false);
        ThisAnimator.SetBool("Chase", false);
        ThisAnimator.SetBool("Walk", true);
        PatrolDestination = PatrolDestinations[Random.Range(0, (PatrolDestinations.Length - 1))].transform;
        //Loop while patrolling
        while (currentstate == ENEMY_STATE.PATROL)
        {
            //Set strict search
            ThisLineSight.Sensitity = LineSight.SightSensitivity.STRICT;

            ThisAgent.speed = 1f;

            //Chase to patrol position
            //ThisAgent.Resume();
            ThisAgent.isStopped = false;
            ThisAgent.SetDestination(PatrolDestination.position);

            //Wait until path is computed
            while(ThisAgent.pathPending)
                yield return null;

            if (ThisAgent.remainingDistance < 1.5f)
                PatrolDestination = PatrolDestinations[Random.Range(0, (PatrolDestinations.Length))].transform;

            //If we can see the target then start chasing
            if (ThisLineSight.CanSeeTarget)
            {
                //ThisAgent.Stop();
                ThisAgent.isStopped = true;
                transform.LookAt(GameManager.Instance.EnemyTarget);
                CurrentState = ENEMY_STATE.CHASE;
                yield break;
            }

            //Wait until next frame
            yield return null;
        }
    }

    public IEnumerator AIChase()
    {
        ThisAnimator.SetBool("Attack", false);
        ThisAnimator.SetBool("Chase", true);
        ThisAnimator.SetBool("Walk", false);
        ThisAgent.speed = 1.7f;

        //Loop while chasing
        while (currentstate == ENEMY_STATE.CHASE)
        {
            //transform.LookAt(GameManager.Instance.EnemyTarget);
            //Set loose search
            ThisLineSight.Sensitity = LineSight.SightSensitivity.LOOSE;

            //Chase to last known position
            //ThisAgent.Resume();
            ThisAgent.isStopped = false;
            ThisAgent.SetDestination(ThisLineSight.LastKnowSighting);

            //Wait until path is computed
            while(ThisAgent.pathPending)
                yield return null;

            //Have we reached destination?
            if(ThisAgent.remainingDistance <= ThisAgent.stoppingDistance +0.5f)
            {
                //Stop agent
                ThisAgent.isStopped = true;
                //ThisAgent.Stop();

                //Reached destination but cannot see player
                if(!ThisLineSight.CanSeeTarget)
                    CurrentState = ENEMY_STATE.PATROL;
                else //Reached destination and can see player. Reached attacking distance
                    CurrentState = ENEMY_STATE.ATTACK;

                yield break;
            }

            //Wait until next frame
            yield return null;
        }
    }

    public IEnumerator AIAttack()
    {
        ThisAnimator.SetBool("Attack", true);
        ThisAnimator.SetBool("Chase", false);
        ThisAnimator.SetBool("Walk", false);
        //Loop while chasing and attacking

while (currentstate == ENEMY_STATE.ATTACK)
        {
            //Chase to player position
            ThisAgent.isStopped = false;
ThisAgent.SetDestination(GameManager.Instance.EnemyTarget.position);

            //Wait until path is computed
            while (ThisAgent.pathPending)
                yield return null;

            //Has player run away?
            if(ThisAgent.remainingDistance > ThisAgent.stoppingDistance + 0.5f)
            //if(!inRange)
            {
                //Change back to chase
                CurrentState = ENEMY_STATE.CHASE;
                yield break;
            }
            else
            {
                //Attack
                GameManager.Instance.Player.TakeDamage(MaxDamage);
                sound = attacksSounds[Random.Range(0, (attacksSounds.Length))];
                ThisAudioSource.PlayOneShot(sound);
            }

            //Wait until next frame
            yield return attackDelay;
        }

        yield break;
    }

    //Called when the enemy is defeated and can no longer move
    public void Defeated()
    {
        Debug.Log("DEFEATED");
        //Disable the navmesh agent
        ThisAgent.enabled = false;
        ThisAnimator.SetBool("Die", true);
        SpawnableManager.informSpawnableDestroyed(gameObject, false);
        CurrentState = ENEMY_STATE.DEAD;
        EnemyManager.nbrZombies --;
        EnemyManager.CountAllZombie();
    }

    public IEnumerator StartZombie()
    {
        yield return new WaitForSeconds(5);
        CurrentState = ENEMY_STATE.PATROL;
    }

}

Code for Line of Sight :

using UnityEngine;
using System.Collections;
//------------------------------------------
public class LineSight : MonoBehaviour
{
    //------------------------------------------
    //How sensitive should we be to sight
    public enum SightSensitivity {STRICT, LOOSE};

    //Sight sensitivity
    public SightSensitivity Sensitity = SightSensitivity.STRICT;

    //Can we see target
    public bool CanSeeTarget = false;

    public bool DebugFOV = false;

    //FOV
    public float FieldOfView = 120f;

    //Reference to target
    public Transform Target = null;

    //Reference to eyes
    public Transform EyePoint = null;

    //Reference to transform component
    private Transform ThisTransform = null;

    //Reference to sphere collider
    public SphereCollider ThisCollider = null;

    //Reference to last know object sighting, if any
    public Vector3 LastKnowSighting = Vector3.zero;

    private Vector3 DirToTarget;


    void Awake()
    {
        ThisTransform = GetComponent<Transform>();
        ThisCollider = GetComponent<SphereCollider>();
        LastKnowSighting = ThisTransform.position;
    }
    private void Start()
    {
        Target = GameManager.Instance.EnemyTarget;
    }

    //------------------------------------------
    bool InFOV()
    {
        //Get direction to target
        DirToTarget = Target.position - EyePoint.position;

        //Get angle between forward and look direction
        float Angle = Vector3.Angle(EyePoint.forward, DirToTarget);

        //Are we within field of view?
        if(Angle <= FieldOfView)
        {
            Debug.DrawRay(EyePoint.position, (Target.position - EyePoint.position), Color.cyan);
            return true;
        }



        //Not within view
        return false;
    }
    //------------------------------------------
    bool ClearLineofSight()
    {
        RaycastHit Info;
        if (Physics.Raycast(EyePoint.position, (Target.position - EyePoint.position), out Info, ThisCollider.radius *2))
        {
            //If player, then can see player
            //if (Info.transform.CompareTag("MainCamera"))
            if(Info.transform.gameObject.layer == LayerMask.NameToLayer("Gringan"))
                return true;
        }

        return false;
    }
    //------------------------------------------
    void UpdateSight()
    {
        switch(Sensitity)
        {
            case SightSensitivity.STRICT:
                CanSeeTarget = InFOV() && ClearLineofSight();
            break;

            case SightSensitivity.LOOSE:
                CanSeeTarget = InFOV() || ClearLineofSight();
            break;
        }
    }
    //------------------------------------------
    void OnTriggerStay(Collider Other)
    {
        UpdateSight();

        //Update last known sighting
        if(CanSeeTarget)
            LastKnowSighting =  Target.position;
    }

    void OnDrawGizmos()
    {
        float totalFOV = 120.0f;
        float rayRange = 3.9f;
        float halfFOV = totalFOV / 2.0f;
        Quaternion leftRayRotation = Quaternion.AngleAxis(-halfFOV, Vector3.up);
        Quaternion rightRayRotation = Quaternion.AngleAxis(halfFOV, Vector3.up);
        Vector3 leftRayDirection = leftRayRotation * transform.forward;
        Vector3 rightRayDirection = rightRayRotation * transform.forward;
        Gizmos.color = Color.red;
        Gizmos.DrawRay(transform.position, leftRayDirection * rayRange);
        Gizmos.DrawRay(transform.position, rightRayDirection * rayRange);
    }

    private void Update()
    {
        if(CanSeeTarget)
            Debug.DrawRay(EyePoint.position, (Target.position - EyePoint.position), Color.yellow);
    }

Thanks for helping me.

Firstly, you should be able to get the stack trace from your stack overflow, which would help a lot in tracking down these issues.

Although I don't know exactly what you're trying to do here, the most likely cause of a stack overflow from your code here is an infinite swapping between the attacking and chasing states.

If you follow the code through, currently chasing goes to attacking if range is less than or equal to a number, and attacking goes to chasing if range is more than that number.

Those conditions work currently, as they are mutually exclusive. If you were to change one of them to work off a trigger however (removing the mutual exclusivity), then you would have the potential to infinitely change state back and forth.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM