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.