Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

A* script following multiple targets

Discussion in 'Scripting' started by JordanLDJobs, Dec 12, 2019.

  1. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    So I'm setting up a pathfinding script and it's working perfectly with getting to a single target. I'm looking to expand on this and have the character get to two targets, then loop back and forth between the two.



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Unit : MonoBehaviour
    6. {
    7.     public Transform target;
    8.     float speed = 5;
    9.     Vector3[] path;
    10.     int targetIndex;
    11.  
    12.     void Start()
    13.     {
    14.         PathRequestManager.RequestPath(transform.position, target.position, OnPathFound);
    15.     }
    16.  
    17.     public void OnPathFound(Vector3[] newPath, bool pathSuccessful)
    18.     {
    19.         if (pathSuccessful)
    20.         {
    21.             path = newPath;
    22.             StopCoroutine("FollowPath");
    23.             StartCoroutine("FollowPath");
    24.         }
    25.     }
    26.  
    27.  
    28.     IEnumerator FollowPath()
    29.     {
    30.         Vector3 currentWaypoint = path[0];
    31.  
    32.         while (true)
    33.         {
    34.             if (transform.position == currentWaypoint)
    35.             {
    36.                 targetIndex++;
    37.                 if (targetIndex >= path.Length)
    38.                 {
    39.                     targetIndex = 0;
    40.                     path = new Vector3[0];
    41.                     yield break;
    42.                 }
    43.                 currentWaypoint = path[targetIndex];
    44.             }
    45.  
    46.             transform.position = Vector3.MoveTowards(transform.position, currentWaypoint, speed * Time.deltaTime);
    47.  
    48.             yield return null;
    49.         }
    50.     }
    51.  
    52.     public void OnDrawGizmos()
    53.     {
    54.         if (path !=null)
    55.         {
    56.             for (int i = targetIndex; i < path.Length; i ++)
    57.             {
    58.                 Gizmos.color = Color.black;
    59.                 Gizmos.DrawCube(path[i], Vector3.one);
    60.  
    61.                 if (i == targetIndex)
    62.                 {
    63.                     Gizmos.DrawLine(transform.position, path[i]);
    64.                 }
    65.                 else
    66.                 {
    67.                     Gizmos.DrawLine(path[i - 1], path[i]);
    68.                 }
    69.             }
    70.         }
    71.     }
    72. }
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    OK, so what's the question? Is this your attempt at a solution and there's a problem (and if so, what's the problem?)? Or, is this your single-target working solution and you don't know how to change it?
     
  3. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    Hi man, yeah this is all working fine with getting to a single target, I'm looking to change it for two targets
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,385
    A* doesn't find paths to 2 targets. It finds a path between 2 points in a graph (start->finish).

    If you want to find multiple paths, you need to compute A* for each path. Which means you need to decide the course of action in picking your goals.

    For example do we head to the first target, then upon reaching, head to the second target? Which is the first and which the second? Do we want the path chosen to be influenced by both targets? (this can be done with have proximity weights where it prefers a path that is close to BOTH targets... that is if you wrote an algorithm that allows passing in a dynamic weight system).

    What is your desired method of approach? How do you want your mob to behave?
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,385
    Also, I looked at your code... the supplied code isn't the A* implementation. But rather the script that reacts to the code returned by your A* implementation.

    That A* implementation seems to be accessed by the static method PathRequestManager.RequestPath.

    Where is this from? What library are you using?

    I googled around for any classes named that and found a few githubs with various implementations. But only this one had a static method called RequestPath in the shape yours is in your example code:
    https://github.com/olokobayusuf/3D-A-Star-Pathfinding/blob/master/Pathfinding/PathRequestManager.cs

    Is that the library you're using?

    If you use 3rd party code, you should probably include that information along with your post. Most people won't go hunting it down like I did.

    Also, looking at that library, it doesn't appear to support weights or your own custom heuristic (which could let you hack weights in). It seems like a very barebone implementation.
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    If I understand correctly, the goal is to just go to multiple waypoints in sequence, like a patrol route, right?

    So, you'll need to add an array (Transform[] targets) representing your points, and an int representing which target you're finding right now. Now, replace any reference to "target" with "targets[targetIndex]".

    Now, find out what code runs when you've reached the end of a path. Looks like that's around line 40, just before you "yield break". At that point, you need to:
    1) Increment targetIndex
    2) Make targetIndex loop back to 0 if needed, for which you should use the modulo operator (%)
    3) Request a new path

    So something like:
    Code (csharp):
    1. ...
    2. path = new Vector3[0];
    3. targetIndex = (targetIndex + 1) % targets.Length;
    4. PathRequestManager.RequestPath(transform.position, targets[targetIndex].position, OnPathFound);
    5.  
    6. yield break;
    Shouldn't take much more than that. You ought to now be able to drag-assign a bunch of targets into your array in the inspector and it should follow them one after another.
     
  7. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    Could you give me an example of the Array and int parts, if it's not too much trouble?
     
  8. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    Code (csharp):
    1. public Transform[] targets;
    2. public int targetIndex=0;
     
  9. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    Hey this is working! Thank you so much, my only issue is that it only follows 2 targets now, and stops if I add a third although I only needed 2 targets, be cool to have the option for more there, I believe it may be something related to target index?
     
  10. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    Getting an IndexOutOfRangeException: Index was outside the bounds of the array error
     
  11. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    Hm, those are both weird and shouldn't happen. The index should count up by 1 until it hits targets.Length at which point it goes back to 0 (that's what the line with the % symbol does). Could you paste the script as it is now and see if something got lost in translation?
     
  12. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    Sure, I think I've probably forgotten to do something

    Code (CSharp):
    1. public class Unit : MonoBehaviour
    2. {
    3.     public Transform[] targets;
    4.     float speed = 20;
    5.     Vector3[] path;
    6.     public int targetIndex = 0;
    7.  
    8.     void Start()
    9.     {
    10.         PathRequestManager.RequestPath(transform.position, targets[targetIndex].position, OnPathFound);
    11.     }
    12.  
    13.     public void OnPathFound(Vector3[] newPath, bool pathSuccessful)
    14.     {
    15.         if (pathSuccessful)
    16.         {
    17.             path = newPath;
    18.             StopCoroutine("FollowPath");
    19.             StartCoroutine("FollowPath");
    20.         }
    21.     }
    22.  
    23.  
    24.     IEnumerator FollowPath()
    25.     {
    26.         Vector3 currentWaypoint = path[0];
    27.  
    28.         while (true)
    29.         {
    30.             if (transform.position == currentWaypoint)
    31.             {
    32.                 targetIndex++;
    33.                 if (targetIndex >= path.Length)
    34.                 {
    35.                     targetIndex = 0;
    36.                     path = new Vector3[0];
    37.                     targetIndex = (targetIndex + 1) % targets.Length;
    38.                     PathRequestManager.RequestPath(transform.position, targets[targetIndex].position, OnPathFound);
    39.                     yield break;
    40.                 }
    41.                 currentWaypoint = path[targetIndex];
    42.             }
    43.  
    44.             transform.position = Vector3.MoveTowards(transform.position, currentWaypoint, speed * Time.deltaTime);
    45.  
    46.             yield return null;
    47.         }
    48.     }
    49.  
    50.     public void OnDrawGizmos()
    51.     {
    52.         if (path !=null)
    53.         {
    54.             for (int i = targetIndex; i < path.Length; i ++)
    55.             {
    56.                 Gizmos.color = Color.black;
    57.                 Gizmos.DrawCube(path[i], Vector3.one);
    58.  
    59.                 if (i == targetIndex)
    60.                 {
    61.                     Gizmos.DrawLine(transform.position, path[i]);
    62.                 }
    63.                 else
    64.                 {
    65.                     Gizmos.DrawLine(path[i - 1], path[i]);
    66.                 }
    67.             }
    68.         }
    69.     }
    70. }
     
  13. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    Oh, you know what, when I wrote that I didn't even realize you already had a variable named "targetIndex" in your original script. That's the problem! Sorry about that.

    Pick a new name for all instances of "targetIndex" that's in the code I wrote.
     
  14. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    Do I keep your Array and Int variables? Or go back to what I had? and do you mean just go over your code and rename all targetIndex with something else? Like targetArray for example? I'd need to add a new reference at the top then wouldn't I? like an Int
     
  15. JordanLDJobs

    JordanLDJobs

    Joined:
    Nov 24, 2018
    Posts:
    8
    NVM I've done it, working fine, thank you so much :)