Smarter AI Searching for the Player?

I’ve been trying to set up the enemy Ai to take the player’s last known direction and use that for a way to check specific waypoints in the level depending on what that last direction was. Does anyone have experience with enemy AI searching for the player?

void Chasing ()
{
// Create a vector from the enemy to the last sighting of the player.
Vector3 sightingDeltaPos = enemySight.personalLastSighting - transform.position;
// If the the last personal sighting of the player is not close...
if(sightingDeltaPos.sqrMagnitude > 4f)
// ... set the destination for the NavMeshAgent to the last personal sighting of the player.
nav.destination = enemySight.personalLastSighting;
Quaternion rotation = Quaternion.LookRotation(enemySight.personalLastSighting);
if(nav.remainingDistance < nav.stoppingDistance)
{
//??????
}
if(lastPlayerSighting.position != lastPlayerSighting.resetPosition) // if the alarm goes off then chaseSpeed
{
nav.speed = chaseSpeed;
}
else if(lastPlayerSighting.position == lastPlayerSighting.resetPosition) //if the alarm doesn't go off then alertSpeed
{
nav.speed = alertSpeed;
}
// If near the last personal sighting...
if(nav.remainingDistance < nav.stoppingDistance)
{
// ... increment the timer.
chaseTimer += Time.deltaTime;
// If the timer exceeds the wait time...
if(chaseTimer >= chaseWaitTime)
{
// ... reset last global sighting, the last personal sighting and the timer.
lastPlayerSighting.position = lastPlayerSighting.resetPosition;
enemySight.personalLastSighting = lastPlayerSighting.resetPosition;
chaseTimer = 0f;
}
}
else
// If not near the last sighting personal sighting of the player, reset the timer.
chaseTimer = 0f;
}

Do you have a specific question or are you opening this thread for general discussion of the topic? (If your question is “Does anyone have experience?” the answer is yes. ;))

Consider separating responsibilities, perhaps into three scripts:

  • Navigation: Given a destination and speed, simply get there and stop.

  • Target Selection: Given a mental representation of the world, decide where to go and how fast.

  • Perception: Scan the world, keep track of the last player sighting, maintain a representation of the world.

Each script will be easier to understand, maintain, and even swap out with more-sophisticated algorithms in the future without impacting the other scripts. For example, you could swap out your perception script with something that also listens for player sounds, or predicts where the player is heading toward, etc.

At the moment I was just trying a number of different things that might make the job easier. For the moment it’s just kind of a guessing game, checking the documentation for what I can use, playing with code, and using what I’ve already learned to the best of my abilities. I already have the enemy moving to the last position of the player (while the player was in the enemy trigger zone). I tried to use a navMesh hit but I couldn’t get it to change the enemy’s destination. No error’s, he just ignores it.

edit alright I found out it had to do with the patrol function being called. Have to find some way around that, before I can do anything. Here’s the actual script from the tutorial http://unity3d.com/learn/tutorials/projects/stealth/enemy-ai

// If near the last personal sighting...
        if(nav.remainingDistance < nav.stoppingDistance)
        {
            // ... increment the timer.
            chaseTimer += Time.deltaTime;
          
            // If the timer exceeds the wait time...
            if(chaseTimer >= chaseWaitTime)
            {
                NavMeshHit hit;
                if (nav.Raycast(enemySight.personalLastSighting, out hit))
                {
                    if(hit.position == wayPoint.position)
                    {
                        nav.destination = wayPoint.position;
                    }
                }
                // ... reset last global sighting, the last personal sighting and the timer.
                lastPlayerSighting.position = lastPlayerSighting.resetPosition;
                enemySight.personalLastSighting = lastPlayerSighting.resetPosition;
                chaseTimer = 0f;
            }
        }
        else
            // If not near the last sighting personal sighting of the player, reset the timer.
            chaseTimer = 0f;
    }

Been working on this for a while now. Got some of the basics in, like breaking him out of his patrol, and then getting him to go to various search way points I put in the level. Now I want to find the direction the player is going (instead of his position) and have the AI to go to specific waypoints, depending on which direction the player was heading when last detected. Could anyone give me some hints on how I could get that started?

Keep track of the player’s position over time. For example, check once per second. Each second, subtract the player’s current position from his position 3 seconds ago. This will be a vector pointing roughly in the direction the player is heading. Add that vector to the player’s current position. That’s where the player will probably be in 3 seconds. Then choose the waypoint closest to that point.

Thank you, I’ll give this a shot.

To get the vector your adding to the player’s current position, would it be something like this?

posSeconds += Time.deltaTime;

        if (posSeconds >= posSecondsTimer) // 3f
        {
            pastPos = player.transform.position;
            posSeconds = 0;
        }

        Debug.Log (pastPos);

        posLag += Time.deltaTime;
        if(posLag >= posLagTimer) // 3f
        {
            newPos += Time.deltaTime;
            if(newPos >= newPosTimer) // 1f
            {
                relativePos = pastPos - player.transform.position;
                newPos = 0;
            }
        }

        newDirectionVector = player.transform.position + relativePos;

For the vector math, try this:

relativePos = player.transform.position - pastPos;

Otherwise the vector would be pointing backwards. You can also browse Unity’s page on vector arithmetic.

To keep track of position over time, I might modify your approach a little. Perhaps run a looping coroutine that polls the player’s position only once per second.

IEnumerator MyCoroutine() {
    while (chase) {

        // Record player's position from current to 3 seconds back:
        pos3secsAgo = pos2secsAgo;
        pos2secsAgo = pos1secAgo;
        pos1Ago = posNow;
        posNow = <poll player position>;

        // Send the AI to where the player should be 3 seconds in the future:
        pos3secsFromNow = posNow + (posNow - pos3secsAgo);
        nav.destination = pos3secsFromNow;
   
        // Wait for 1 second:
        yield return new WaitForSeconds(1);
    }
}

Parson the excessive variables; I thought it might be clearer in this example than using a different data structure to keep track of the positions over time. It’s just theoretical for discussion, not necessarily the way your real code should be exactly.

This learning tut is helpful. It has last known.

So far this is what I’ve come up with. I’ve tried using the NavMeshEdge like this to find the closest way point…

     NavMeshHit hit;
   
            if (NavMesh.FindClosestEdge(pos3secsFromNow, out hit, 1))
            {
       
                searchWayPoints[searchIndex].position = hit.position;
            }

But for some reason the way points in the searchWayPoints array, assign themselves to (i’m guessing) the “pos3secsFromNow” Vector. So Basically what is happening, is when I actually alert the enemy, All the waypoints are assigned to my player’s “pos3secsFromNow”, but then actually move to the player’s location where he is standing after 3 seconds. There’s obviously a flaw in my logic, so i’m going to try and see where I went wrong.

IEnumerator Location()
    {
        while(enemySight.personalLastSighting != lastPlayerSighting.resetPosition && playerHealth.health > 0f)
        {
            Vector3 sightingDeltaPos = enemySight.personalLastSighting - transform.position;

            Vector3 currentPos = player.transform.position;
             pos3secsFromNow = currentPos + (currentPos - pastPos);

            if(sightingDeltaPos.sqrMagnitude > 4f)
            {
                nav.destination = pos3secsFromNow;
            }
   
            // Wait for 1 second:
            yield return new WaitForSeconds(1);
        }
    }

…Here is the coroutine I used

using UnityEngine;
using System.Collections;

public class EnemyAI : MonoBehaviour
{
    public float patrolSpeed = 2f;                          // The nav mesh agent's speed when patrolling.
    public float alertSpeed = 4f;
    public float chaseSpeed = 5f;                           // The nav mesh agent's speed when chasing.
    public float chaseWaitTime = 5f;                        // The amount of time to wait when the last sighting is reached.
    public float patrolWaitTime = 1f;                       // The amount of time to wait when the patrol way point is reached.
    public Transform[] patrolWayPoints;                     // An array of transforms for the patrol route.
    public Transform[] searchWayPoints;

    private NavMesh mesh;

    private float posSeconds;
    private float posSecondsTimer = 3f;
    private Vector3 pastPos;
    private Vector3 pos3secsFromNow;

    private EnemySight enemySight;                          // Reference to the EnemySight script.
    private EnemySight playerinSight;
    private NavMeshAgent nav;                               // Reference to the nav mesh agent.
    private Transform player;                               // Reference to the player's transform.
    private PlayerHealth playerHealth;                      // Reference to the PlayerHealth script.
    private LastPlayerSighting lastPlayerSighting;          // Reference to the last global sighting of the player.
    private float chaseTimer;                               // A timer for the chaseWaitTime.
    private float patrolTimer;                              // A timer for the patrolWaitTime.
    private int wayPointIndex;                              // A counter for the way point array.
    private int searchIndex;


    void Awake ()
    {
        // Setting up the references.
        enemySight = GetComponent<EnemySight>();
        nav = GetComponent<NavMeshAgent>();
        player = GameObject.FindGameObjectWithTag(Tags.player).transform;
        playerHealth = player.GetComponent<PlayerHealth>();
        lastPlayerSighting = GameObject.FindGameObjectWithTag(Tags.gameController).GetComponent<LastPlayerSighting>();
    }


    void Update ()
    {
       StartCoroutine(Location());

       posSeconds += Time.deltaTime;
   
       if (posSeconds >= posSecondsTimer) // 3f
       {
           pastPos = player.transform.position;
           posSeconds = 0f;
       }

        // If the player is in sight and is alive...
        if(enemySight.playerInSight && playerHealth.health > 0f)
            // ... shoot.
            Shooting();

        // If the player has been sighted and isn't dead...
        else if(enemySight.personalLastSighting != lastPlayerSighting.resetPosition && playerHealth.health > 0f)
            // ... chase.
            Chasing();

        // Otherwise...
        else
            // ... patrol.
            Patrolling();
    }


    void Shooting ()
    {
        // Stop the enemy where it is.
        nav.Stop();
    }

    void Chasing ()
    {
        for(searchIndex = 0; searchIndex < searchWayPoints.Length; searchIndex ++)
        {

            NavMeshHit hit;
    
            if (NavMesh.FindClosestEdge(pos3secsFromNow, out hit, 1))
            {
        
                searchWayPoints[searchIndex].position = hit.position;
            }
    
            // Set the appropriate speed for the NavMeshAgent.
            nav.speed = chaseSpeed;
    
            // ... increment the timer.
            chaseTimer += Time.deltaTime;
    
            // If the timer exceeds the wait time...
            if(chaseTimer >= chaseWaitTime)
            {
        
                // ... reset last global sighting, the last personal sighting and the timer.
                lastPlayerSighting.position = lastPlayerSighting.resetPosition;
                enemySight.personalLastSighting = lastPlayerSighting.resetPosition;
                chaseTimer = 0f;
            }
    
            else
            {
                // If not near the last sighting personal sighting of the player, reset the timer.
                chaseTimer = 0f;
            }
        }
    }


    void Patrolling ()
    {
        // Set an appropriate speed for the NavMeshAgent.
        nav.speed = patrolSpeed;

        // If near the next waypoint or there is no destination...
        if(nav.destination == lastPlayerSighting.resetPosition || nav.remainingDistance < nav.stoppingDistance)
        {
            // ... increment the timer.
            patrolTimer += Time.deltaTime;
    
            // If the timer exceeds the wait time...
            if(patrolTimer >= patrolWaitTime)
            {
                // ... increment the wayPointIndex.
                if(wayPointIndex == patrolWayPoints.Length - 1)
                    wayPointIndex = 0;
                else
                    wayPointIndex++;
        
                // Reset the timer.
                patrolTimer = 0;
            }
        }
        else
            // If not near a destination, reset the timer.
            patrolTimer = 0;

        // Set the destination to the patrolWayPoint.
        nav.destination = patrolWayPoints[wayPointIndex].position;
    }

    IEnumerator Location()
    {
        while(enemySight.personalLastSighting != lastPlayerSighting.resetPosition && playerHealth.health > 0f)
        {

            Vector3 sightingDeltaPos = enemySight.personalLastSighting - transform.position;


            Vector3 currentPos = player.transform.position;
            pos3secsFromNow = currentPos + (currentPos - pastPos);

            if(sightingDeltaPos.sqrMagnitude > 4f)
            {
                nav.destination = pos3secsFromNow;
            }
    
            // Wait for 1 second:
            yield return new WaitForSeconds(1);
        }
    }
}

Yes it is. This is the code i’m trying to modify

Here’s something slightly different where the AI stops at the last known location of the player and then is “supposed to” move to the closest waypoint of the “pos3secsFromNow” position. Tinker with it if you want, see if you can come up with anything.

void Chasing ()
    {
        Vector3 sightingDeltaPos = enemySight.personalLastSighting - transform.position;

        if(sightingDeltaPos.sqrMagnitude > 4f)
            // ... set the destination for the NavMeshAgent to the last personal sighting of the player.
            nav.destination = enemySight.personalLastSighting;

        chaseTimer += Time.deltaTime;

        if(chaseTimer > chaseWaitTime)
        {
            chase = true;

            for(searchIndex = 0; searchIndex < searchWayPoints.Length; searchIndex ++)
            {
                NavMeshHit hit;
               
                if(NavMesh.FindClosestEdge(pos3secsFromNow, out hit,1))
                {
                   
                    searchWayPoints[searchIndex].position = hit.position;
                }
            }
        }
        // Set the appropriate speed for the NavMeshAgent.
        nav.speed = chaseSpeed;
       
   
    }
IEnumerator Location()
    {
        while(chase == true)
        {
            
           
            Vector3 currentPos = player.transform.position;
           
           
            pos3secsFromNow = currentPos + (currentPos - pastPos);
       
            nav.destination = pos3secsFromNow;
           
            // Wait for 1 second:
            yield return new WaitForSeconds(1);
        }
    }

Using UnitySteer .
apply astar path finding algorithm for finding a closest point or position ,
Divide your AI into States => switch case
define your distance ,
mean
if distance<1 then state one is doing
even if you do anything and what you want in AI system created .
for
2D only.


benefits :-
1:- No use of collider
2:- Movement based on rigidbody.
if you need Ai based on Transform then do also this step by your side