Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
0
Question by ThePhoenixSaga32 · Sep 08, 2017 at 12:43 AM · aienemywaypoint systemfinite-state-machine

Enemy AI keeps chasing player even after the player has left its line of sight.

Hello and thank you for taking the time to read this question.

I have a prototype of a basic AI which uses finite state machine to switch from patrolling around way-points to chasing the player when in the enemy AI's view. My problem is that the enemy AI keeps chasing the player after the player has left its sight. This shouldn't happen because the enemy AI should be switching back to patrolling when the player has left its line of sight.

Please could someone look at the scripts and let me know what it is that I have done wrong that is breaking the enemy AI's behaviour. Below are the scripts that I am talking about which are used in my prototype.

The LineSight.cs script is responsible for informing the enemy AI if the player is withing its view. This script is attached to the enemy object, but a empty child object called Eye point is is linked to the LineSight.cs script and the object is placed in front of the enemy AI's eyes.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class LineSight : MonoBehaviour
 {
 
     //How sensitive shoudl we be to sight
     public enum SightSensitivity { STRICT, LOOSE }
 
     //Sight sensitivity
     public SightSensitivity Sensitity = SightSensitivity.STRICT;
 
     //can we see target
     public bool CanSeeTarget = false;
 
     //FOV
     public float FieldOfView = 90f;
 
     //reference to target
     private Transform Target = null;
 
     //reference to eyes
     public Transform EyePoint = null;
 
     //reference to transform component
     private Transform ThisTransform = null;
 
     //reference to sphere collider
     private SphereCollider ThisCollider = null;
 
     //reference to last known object sighting, if any
     public Vector3 LastKnowSighting = Vector3.zero;
 
     private void Awake()
     {
         ThisTransform = GetComponent<Transform>();
         ThisCollider = GetComponent<SphereCollider>();
         LastKnowSighting = ThisTransform.position;
         Target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
     }
 
     bool InFOV()
     {
         //get direction to target
         Vector3 DirToTarget = Target.position - EyePoint.position;
 
         //get angle between forward and look direction
         float Angle = Vector3.Angle(EyePoint.forward, DirToTarget);
 
         //are we within feild of view?
         if (Angle <= FieldOfView)
         {
             //Debug.Log("Within View");
             return true;
         }
 
         //not within view
         return false;
     }
 
     bool ClearLineOfSight()
     {
         RaycastHit Info;
         //ThisCollider.radius
         if (Physics.Raycast(EyePoint.position, (Target.position - EyePoint.position).normalized, out Info, 4))
         {
             //if player, then can see player
             if (Info.transform.CompareTag("Player"))
             {
                 //Debug.DrawRay(EyePoint.position, (Target.position - EyePoint.position).normalized, Color.yellow, 100000);
                 Debug.Log("Raycast hit player");
                 return true;
             }
             //Debug.Log("Raycast is hitting something");
         }
 
         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;
             //Debug.Log("I saw you at: "+LastKnowSighting);
         }
     }
 
     void OnTriggerExit(Collider Other)
     {
         if (!Other.CompareTag("Player"))
         {
             return;
         }
         CanSeeTarget = false;
     }
 }
 

The EnemyAI.cs script holds the grunt of the AI code to make the AI move around and switch states. It references the LineSight.cs script to check if the player is within its view, and of that is true, then the EnemyAI.cs script would switch the enemy from patrolling to chasing, and chasing after the player in sight.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.AI;
 
 public class AI_Enemy : MonoBehaviour
 {
 
     public enum ENEMY_STATE { PATROL, CHASE, ATTACK };
 
     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;
             }
         }
     }
 
     [SerializeField]
     private ENEMY_STATE currentState = ENEMY_STATE.PATROL;
 
     //reference to line of sight
     private LineSight ThisLineSight = null;
 
     //reference to nav mesh agent
     private NavMeshAgent ThisAgent = null;
 
     //reference to player health
     //private Health PlayerHealth = null;
 
     //reference to player transform
     private Transform PlayerTransform = null;
 
     //reference to patrol destination
     //public Transform PatrolDestination;
 
     //damage per second
     //public float MaxDamage = 10f;
 
     // For navigation waypoint system
     private List<Transform> points = new List<Transform>();
     private int destPoints = 0;
     public WaypointSystem path;
 
     private void Awake()
     {
         ThisLineSight = GetComponent<LineSight>();
         ThisAgent = GetComponent<NavMeshAgent>();
         ThisAgent.autoBraking = false;
         //PlayerHealth = GameObject.FindGameObjectWithTag("Player").GetComponent<Health>();
         PlayerTransform = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
         //PlayerTransform = PlayerHealth.GetComponent<Transform>();
 
     }
 
     void Start()
     {
         //Get random destination
         //GameObject[] Destinations = GameObject.FindGameObjectsWithTag("Dest");
         //PatrolDestination = Destinations[Random.Range(0, Destinations.Length)].GetComponent<Transform>();
 
         //configure starting state
         CurrentState = ENEMY_STATE.PATROL;
 
         //initalize waypoint system
         points = path.waypoints;
         
     }
 
     void GoToNextPoint()
     {
         //Returns if no points have been set
         if (points.Count == 0)
         {
             return;
         }
         //Set the agent to go to the currently selected destiniation
         ThisAgent.destination = points[destPoints].position;
 
         //Choose the next point in the array as the destination, cycling to the start if necessary
         destPoints = (destPoints + 1) % points.Count;
     }
 
     public IEnumerator AIPatrol()
     {
         //loop while patrolling
         while (currentState == ENEMY_STATE.PATROL)
         {
             //set trict search
             ThisLineSight.Sensitity = LineSight.SightSensitivity.STRICT;
 
             //chase to patrol position
             ThisAgent.isStopped = false;
 
             //ThisAgent.SetDestination(PatrolDestination.position);
             if (!ThisAgent.pathPending && ThisAgent.remainingDistance < 0.5f)
             {
                 GoToNextPoint();
             }
 
             //wait until path is computed
             while (ThisAgent.pathPending)
             {
                 yield return null;
             }
             //If we can see the target then start chasing
             if (ThisLineSight.CanSeeTarget)
             {
                 ThisAgent.isStopped = true;
                 CurrentState = ENEMY_STATE.CHASE;
                 //Debug.Log("I see you....");
                 yield break;
             }
             Debug.Log("I'm patrolling....");
             //wait until next frame
             yield return null;
 
         }
 
         yield break;
     }
 
     public IEnumerator AIChase()
     {
         //loop while chasing
         while (currentState == ENEMY_STATE.CHASE)
         {
             //set loose search
             ThisLineSight.Sensitity = LineSight.SightSensitivity.LOOSE;
 
             //chase to last known position
             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)
             {
                 //stop agent
                 ThisAgent.isStopped = true;
 
                 //reached destination but cannot see player
                 if (!ThisLineSight.CanSeeTarget)
                 {
                     CurrentState = ENEMY_STATE.PATROL;
                     Debug.Log("hm, wonder where he went?");
                 }
                 //reached destination and can see player. reached attacking distance
                 else
                 {
                     CurrentState = ENEMY_STATE.ATTACK;
                     //Debug.Log("I'm going to attack you....");
                 }
                 yield break;
             }
             Debug.Log("I'm chasing you....");
             //wait until next frame
             yield return null;
         }
 
         yield break;
     }
 
     public IEnumerator AIAttack()
     {
         //loop while chasing and attacking
         while (currentState == ENEMY_STATE.ATTACK)
         {
             //chase player to position
             ThisAgent.isStopped = false;
             ThisAgent.SetDestination(PlayerTransform.position);
 
             //wait until path is computed
             while (ThisAgent.pathPending)
             {
                 yield return null;
             }
 
             //has player ran away?
             if (ThisAgent.remainingDistance > ThisAgent.stoppingDistance)
             {
                 //change b ack to chase state
                 currentState = ENEMY_STATE.CHASE;
                 yield break;
             }
             else
             {
                 //attack player
                 //PlayerHealth.HealthPoints =- MaxDamage * Time.deltaTime;
                 Debug.Log("I'm attacking....");
             }
 
             //wait until next frame 
             yield return null;
         }
 
         yield break;
     }
 }

I'm providing the WaypointSystem.cs script to provide context and information about how the waypoint system works. This script is really for constructing and holding waypoints (or path objects in the script).

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 [ExecuteInEditMode]
 public class WaypointSystem : MonoBehaviour {
 
     //Change color of waypoints
     public Color PathObjectColor = Color.green;
     public Color PathLinesColor = Color.red;
 
     //creates list of path objects
     public List<Transform> waypoints = new List<Transform>();
 
     //holds number of path objects
     int PathObjectIndex = 0;
 
     //allows path following to be disabled during play mode, useful for telling AI to stop following
     public bool disableInGame;
 
     // Update is called once per frame
     void Update () {
 
         if (!disableInGame)
         {
             //auto adds path objects as they are made int he editor
             Transform[] PathObjects = GetComponentsInChildren<Transform>();
 
             if (waypoints.Count > 0)
             {
                 waypoints.Clear();
 
                 PathObjectIndex = 0; //defaults to 0 while no path objects exist
 
                 foreach (Transform t in PathObjects)
                 {
                     if (t != transform)
                     {
                         t.name = "PathObj_" + PathObjectIndex.ToString();
                         waypoints.Add(t);
                         PathObjectIndex++;
                     }
                 }
             }
         }
 
         //
 
     }
 
     private void OnDrawGizmos()
     {
         if (waypoints.Count > 0)
         {
             //Draws the path objects
             Gizmos.color = PathObjectColor;
 
             foreach (Transform t in waypoints)
             {
                 Gizmos.DrawSphere(t.position, 1f);
             }
 
             //Draws the connected lines between the path objects
             Gizmos.color = PathLinesColor;
 
             for (int a = 0; a < waypoints.Count - 1; a++)
             {
                 Gizmos.DrawLine(waypoints [a].position, waypoints[a + 1].position);
             }
         }
     }
 
 }
 

Additional info: You may have notice some commented lines of code dotted around in the scripts, that's because this whole prototype is from two different tutorials on making basic AIs, so I've tried to merge the two together to make use of them. This is because one tutorial taught how to make the finite state machine and the line of site scripts, while the other taught how to make a waypoint system based on using lists and the position values of each path object dotted around the map .

Also you may have also noticed some debug.log messages dotted around, that was my attempt to troubleshoot which areas weren't being accessed or used properly. From what I can gather, the Enemy AI simply does not lose track of the player one it is within its view. At then start of the run time, when the enemy AI does not see the player initially, the enemy AI patrols around the map as normal, but the moment the player is within its sight, its pretty much attached to the player like glue. Its like the raycast that's being used to detect the player follows where ever the player is, and even ignores if the player is behind the enemy AI or on the other side of a wall.

This issue confuses me because in a different project file that I used for learning the finite state machine, the AI worked differently. The EnemyAI.cs script was more or less the same in the previous project file, just uses a slightly different navigating system to waypoints. Here is the code of the EnmeyAI.cs script from the previous project:

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.AI;
 
 public class AI_Enemy : MonoBehaviour {
 
     public enum ENEMY_STATE { PATROL, CHASE, ATTACK };
 
     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;
             }
         }
     }
 
     [SerializeField]
     private ENEMY_STATE currentState = ENEMY_STATE.PATROL;
 
     //reference to line of sight
     private LineSight ThisLineSight = null;
 
     //reference to nav mesh agent
     private NavMeshAgent ThisAgent = null;
 
     //reference to player health
     private Health PlayerHealth = null;
 
     //reference to player transform
     private Transform PlayerTransform = null;
 
     //reference to patrol destination
     public Transform PatrolDestination;
 
     //damage per second
     public float MaxDamage = 10f;
 
     private void Awake()
     {
         ThisLineSight = GetComponent<LineSight>();
         ThisAgent = GetComponent<NavMeshAgent>();
         PlayerHealth = GameObject.FindGameObjectWithTag("Player").GetComponent<Health>();
         PlayerTransform = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
         PlayerTransform = PlayerHealth.GetComponent<Transform>();
 
     }
 
     void Start()
     {
         //Get random destination
         GameObject[] Destinations = GameObject.FindGameObjectsWithTag("Dest");
         PatrolDestination = Destinations[Random.Range(0, Destinations.Length)].GetComponent<Transform>();
 
         //configure starting state
         CurrentState = ENEMY_STATE.PATROL;
     }
 
     public IEnumerator AIPatrol()
     {
         //loop while patrolling
         while (currentState == ENEMY_STATE.PATROL)
         {
             //set trict search
             ThisLineSight.Sensitity = LineSight.SightSensitivity.STRICT;
 
             //chase to patrol position
             ThisAgent.isStopped = false;
 
             ThisAgent.SetDestination(PatrolDestination.position);
 
             //wait until path is computed
             while (ThisAgent.pathPending)
             {
                 yield return null;
             }
             //If we can see the target then start chasing
             if (ThisLineSight.CanSeeTarget)
             {
                 ThisAgent.isStopped = true;
                 CurrentState = ENEMY_STATE.CHASE;
                 //Debug.Log("I see you....");
                 yield break;
             }
             //Debug.Log("I'm patrolling....");
             //wait until next frame
             yield return null;
 
         }
 
         yield break;
     }
 
     public IEnumerator AIChase()
     {
         //loop while chasing
         while (currentState == ENEMY_STATE.CHASE)
         {
             //set loose search
             ThisLineSight.Sensitity = LineSight.SightSensitivity.LOOSE;
 
             //chase to last known position
             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)
             {
                 //stop agent
                 ThisAgent.isStopped = true;
 
                 //reached destination but cannot see player
                 if (!ThisLineSight.CanSeeTarget)
                 {
                     CurrentState = ENEMY_STATE.PATROL;
                     //Debug.Log("hm, wonder where he went?");
                 }
                 //reached destination and can see player. reached attacking distance
                 else
                 {
                     CurrentState = ENEMY_STATE.ATTACK;
                     //Debug.Log("I'm going to attack you....");
                 }
                 yield break;
             }
             //Debug.Log("I'm chasing you....");
             //wait until next frame
             yield return null;
         }
 
         yield break;
     }
 
     public IEnumerator AIAttack()
     {
         //loop while chasing and attacking
         while (currentState == ENEMY_STATE.ATTACK)
         {
             //chase player to position
             ThisAgent.isStopped = false;
             ThisAgent.SetDestination(PlayerTransform.position);
 
             //wait until path is computed
             while (ThisAgent.pathPending)
             {
                 yield return null;
             }
 
             //has player ran away?
             if (ThisAgent.remainingDistance > ThisAgent.stoppingDistance)
             {
                 //change b ack to chase state
                 currentState = ENEMY_STATE.CHASE;
                 yield break;
             }
             else
             {
                 //attack player
                 PlayerHealth.HealthPoints =- MaxDamage * Time.deltaTime;
                 //Debug.Log("I'm attacking....");
             }
 
             //wait until next frame 
             yield return null;
         }
 
         yield break;
     }
 }

Its probably worth mentioning that in the tutorial for finite state machines, the raycast in LineSight.cs used the sphere collider to determine the range of the sight, this did not work in my prototype, so I went for number values which kinda of worked. Below is an extract from the tutorial on what I'm on about:

 if( Physics.Raycast( EyePoint.position, (Target.position - EyePoint.position). normalized, out Info, ThisCollider.radius))
 

ThisCollider.radius references the GetComponent function, in Awake().

A sphere collider is being used on the enemy AI object and is scaled just around the enemy AI, which kinda of works for the time being, so once the player is inside the sphere collider, the enemy AI switches to the chase state,.

So after digesting all that information, what I want to know is a solution to fix the Enemy AI's line of sight, so instead, when the player is out of its line of sight, such as beyond the sphere collider or behind a wall, the enemy AI ceases to chase the player and restores back to patrolling again.

If there are any issues or questions, please let me know.

Thank you.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

157 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Several NPC with waypoints 1 Answer

Animations not playing correctly for FPS enemy AI 0 Answers

Enemy 2D check for collision 1 Answer

I need the Nav Mesh Stop , I need the Animation to Play Javascript 0 Answers

How to make enemy chase player. Basic AI 7 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges