Unity AI攻击简单的FSM Stackoverflow错误

时间:2017-10-12 14:27:50

标签: c# unity3d artificial-intelligence unity5 game-physics

我让我的僵尸徘徊,在LOS&上追逐我FOV但是当他们到达我(Camera Rig Vive VR)时,只有一个攻击我(不是eveytime但是他尝试)。一旦他们离我很近,我想让他们全部攻击我(最好的是他们绕着我转了一圈)。

如果我尝试改变距离或使用触发器对撞机将布尔值传递给true而不是计算剩余距离(在攻击协程中),我有堆栈溢出错误。我不明白为什么。如果有人能解释我,那将是非常好的。

以下是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;
    }

}

视线代码:

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);
    }

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

首先,您应该能够从堆栈溢出中获取堆栈跟踪,这将有助于追踪这些问题。

虽然我不确切地知道你在这里尝试做什么,但是代码中堆栈溢出的最可能原因是攻击和追逐状态之间的无限交换。

如果您按照代码进行操作,如果范围小于或等于某个数字,则当前追逐会进行攻击,如果范围超过该数字,则攻击会进行追逐。

这些条件目前有效,因为它们是相互排斥的。如果你要改变其中一个来解决触发问题(去除互斥性),那么你就有可能来回无限地改变状态。