How can I make an enemy follow a trail created by a Player

Hello.
I’m creating an enemy pathfinding system that follows a trail of GameObjecs left by the player. However, I’m running into a problem where the enemies stop for a while when they reach the closest GameObject.
Also, I don’t thnk my method for finding the closest GameObject in the trail works correctly, because the Enemies don’t follow the trail exactly and sometimes go to a position really far along the trail.

    public void EnemyAI_Move()
    {
        int moveValue;
        closestChosenPosition = FindClosestLocation();

        if(Vector3.Distance(transform.position, closestChosenPosition.transform.position) >= 0.5f)
        {
            if (Vector3.Distance(transform.position, player.position) <= 50 && Vector3.Distance(transform.position, player.position) >= 1)
            {

                Vector3 movePosition = (closestChosenPosition.transform.position - transform.position).normalized;
                characterController.Move(movePosition * speed * Time.deltaTime);
            }
        }
        else
        {
            closestChosenPosition = FindClosestLocation();
        }

        Quaternion rotation = Quaternion.LookRotation(closestChosenPosition.transform.position - transform.position);
        transform.rotation = rotation;


        if (characterController.velocity != Vector3.zero)
        {
            moveValue = 1;
        }
        else
        {
            moveValue = 0;
        }

        anim.SetFloat("Velocity", moveValue);
    }

    GameObject FindClosestLocation()
    {
        List<float> distances = new List<float>();

        foreach(GameObject obj in FindObjectsByType<GameObject>(FindObjectsSortMode.None))
        {
            if(obj != null && obj.name.Contains("PlayerLocation"))
            {
                float objDistance = Vector3.Distance(transform.position, obj.transform.position);


                distances.Add(objDistance);

                if(objDistance == distances.Min())
                {
                    GameObject closestLocation = obj;
                    return closestLocation;
                 
                }
            }
        }

        return null;
     

 
    }

Ok, I fixed up the FindClosestLocation method.

   void FindClosestLocation()
   {
       float distance = 1000; //Start with a big number


       foreach(GameObject obj in FindObjectsByType<GameObject>(FindObjectsSortMode.InstanceID))
       {
           if(obj != null && obj.name.Contains("PlayerLocation"))
           {
               float objDistance = Vector3.Distance(transform.position, obj.transform.position); //for each object we are determining if they are less than the distance, if they are the object is made the new distance, if an object is closer than that distance, the new distance is set and so on.
             
               if(objDistance < distance)
               {
                   if(Vector3.Distance(transform.position, obj.transform.position) >= 1.5f && obj != closestChosenPosition)
                   {
                     
                       closestChosenPosition = obj;
                       distance = objDistance;
                   }

               }


           }
       }

 
   }

Now, my issue is finding out which closest object is newer using instance IDs. Because when there are two GameObjects at the same distance, I need to find which is newer so the enemy goes to that.

Your code and method just doesn’t make a whole lot of sense.
A better solution might be to have a list which stores the player positions, rather than iterating though every single game object in the scene and trying to use some InstanceID.
Entries at the end of the list are newer entries.

List<Vector3> playerPositions;

public Vector3 FindClosestLocation ()
    {
        var closestDistance = float.MaxValue;
        int closestIndex = -1;

        int i = 0;
        foreach (Vector3 playerPositionEntry in playerPositions) {

            var dist = Vector3.Distance (transform.position, playerPositionEntry);
            if (dist < closestDistance) {
                closestDistance = dist;
                closestIndex = i++;
            }     
        }

        Vector3 closestPosition = playerPositions [closestIndex];
        return closestPosition;
    }

I tried this, but the enemy just gets stuck and stops moving

public Vector3 FindClosestLocation()
{

    float distance = float.MaxValue; //Start with a big number so we can go down
    int closestIndex = 0;

    int i = 0;
    foreach(GameObject playerPositionEntry in GameManager.instance.playerLocators)
    {
        var dist = Vector3.Distance(transform.position, playerPositionEntry.transform.position);
        if (dist < distance)
        {
            distance = dist;
            closestIndex = i++;
        }
    }
    Vector3 closestLocation = GameManager.instance.playerLocators[closestIndex].transform.position;
    return closestLocation;

}

I think this code has a bug with how it updates closestIndex. In your foreach loop, the value “i” only updates when a new shorter distance is found and you use “i” to set closestIndex. This means that if several list entries are processed that are further away, then “i” will no accurately represent the index into the list.

It may be simpler to use a for loop.

    List<Vector3> playerPositions;

    public Vector3 FindClosestLocation()
    {
        var closestDistance = float.MaxValue;
        int closestIndex = -1;

        for (int i = 0; i < playerPositions.Count; i++)
        {
            var dist = Vector3.Distance(transform.position, playerPositions[i]);
            if (dist < closestDistance)
            {
                closestDistance = dist;
                closestIndex = i;
            }
        }

        Vector3 closestPosition = playerPositions[closestIndex];
        return closestPosition;
    }
1 Like

Still doesn’t work. The enemy just gets stuck when it reaches its destination (stops)

  public Vector3 FindClosestLocation()
  {

       float distance = float.MaxValue; //Start with a big number so we can go down
       int closestIndex = -1;

       for(int i =  0; i < GameManager.instance.playerLocators.Count; i++)
       {
            var dist = Vector3.Distance(transform.position, GameManager.instance.playerLocators[i].transform.position);
            if (dist < distance)
            {

                  distance = dist;
                  closestIndex = i;

           
            }
       }
       Vector3 closestLocation = GameManager.instance.playerLocators[closestIndex].transform.position;
       return closestLocation;

  }

Man you really need to learn to debug things on your end. Have you considered if the issue is elsewhere? All that method does is find the closest location.

Start to debug to debug upwards through the logic.

1 Like

Good catch.

If the enemy reaches the closest point, then calling FindClosestLocation() again will just give you the same position you currently just arrived to.
At some point you should remove all elements in the array/list that which are older and equal to the position you’ve just reached before you call FindClosestLocation() again.

Ex:
If you list is: pos1, pos2, pos3, pos4
And the enemy goes to pos2, then pos1 and pos2 should be removed from the list/array.

1 Like

UPDATE: I got it working!

Here’s the full code:

   public void EnemyAI_Move() 
   {

       if(!followingPath && !followingPlayer)
       {
           closestChosenPosition = FindClosestLocation();
           followingPath = true;
       }

       if(closestChosenPosition == null)
       {
           followingPath = false;
       }



       if(closestChosenPosition != null)
       {
           if (Vector3.Distance(transform.position, closestChosenPosition.transform.position) <= 1f)
           {
               visitedPositions.Add(closestChosenPosition);
               closestChosenPosition = FindClosestLocation();
           }
           if (Vector3.Distance(transform.position, player.position) <= 50 && Vector3.Distance(transform.position, player.position) >= 1)
           {

               Vector3 movePosition;
               if (Vector3.Distance(transform.position, player.transform.position) <= 5f)
               {
                   followingPath = false;
                   followingPlayer = true;

                   movePosition = (player.transform.position - transform.position).normalized;           
                  
                   Quaternion rotation = Quaternion.LookRotation(player.transform.position - transform.position);
                   transform.rotation = rotation;

                   Debug.DrawLine(transform.position, player.transform.position, Color.red);

               }
               else
               {
                    followingPlayer = false;

                    movePosition = (closestChosenPosition.transform.position - transform.position).normalized;
                    Quaternion rotation = Quaternion.LookRotation(closestChosenPosition.transform.position - transform.position);
                    transform.rotation = rotation;
               }
              
               characterController.Move(movePosition * speed * Time.deltaTime);
               Debug.DrawLine(transform.position, closestChosenPosition.transform.position, Color.green);

           }
         


       }

       int moveValue;
       if (characterController.velocity != Vector3.zero)
       {
           moveValue = 1;
       }
       else
       {
           moveValue = 0;
       }

       anim.SetFloat("Velocity", moveValue);

   }

   public GameObject FindClosestLocation()
   {

       float distance = float.MaxValue; //Start with a big number so we can go down
       int closestIndex = -1;
      
       allPositions = GameManager.instance.playerLocators.ToArray();
      
       positions.Clear();
       foreach (GameObject i in allPositions)
       {
           if (!visitedPositions.Contains(i))
           {
               positions.Add(i);
           }
       }    

       for (int i = 0; i < positions.Count; i++)
       {
           var dist = Vector3.Distance(transform.position, positions[i].transform.position);
           if (dist < distance)
           {
               distance = dist;
               closestIndex = i;
           }
       }

       GameObject closestLocation = positions[closestIndex];
       return closestLocation;

   }