MultiTargetPath Implementation


#1

Hi everyone,
I’m writing about multi-targets pathfinding implementation. In particular, given a group of targets, I should find the closest target to the player and move my player towards it.
In my hierarchy there are:

  • A* with AStar Path component (Recast Graph);
  • Player with AI Path, AI Destination Setter and Seeker components;
  • Target1 (for example a sphere);
  • Target2 (for example a sphere, the closest one);
  • Target3 (for example a sphere).
    In the single target scene, I drag and drop the target game object whitin the AI Destination Setter and it works. Now, with three possible targets, how can I find the closest one (for example Target2)?
    I have read a tried to apply the example of the documentation (https://arongranberg.com/astar/docs/multitargetpathexamplecs.html) many and many times but I can’t understand where to add this script:

"
using UnityEngine;
using Pathfinding;
using System.Collections.Generic;

public class MultiTargetPathExample : MonoBehaviour {
void Start () {
// Find the Seeker component
Seeker seeker = GetComponent();

    // Set the target points to all children of this GameObject
    Vector3[] endPoints = new Vector3[transform.childCount];
    int c = 0;

    foreach (Transform child in transform) {
        endPoints[c] = child.position;
        c++;
    }

    // Start a multi target path
    seeker.StartMultiTargetPath(transform.position, endPoints, true, OnPathComplete);

    // Alternatively you can create a MultiTargetPath from scratch instead
    // MultiTargetPath p = MultiTargetPath.Construct(transform.position, endPoints, null, null);
    // seeker.StartPath(p, OnPathComplete);
}

public void OnPathComplete (Path p) {
    Debug.Log("Got Callback");

    if (p.error) {
        Debug.Log("Ouch, the path returned an error\nError: " + p.errorLog);
        return;
    }

    MultiTargetPath mp = p as MultiTargetPath;
    if (mp == null) {
        Debug.LogError("The Path was no MultiTargetPath");
        return;
    }

    // All paths
    List<Vector3>[] paths = mp.vectorPaths;

    for (int i = 0; i < paths.Length; i++) {
        // Plot path i
        List<Vector3> path = paths[i];

        if (path == null) {
            Debug.Log("Path number "+i+" could not be found");
            continue;
        }

        var color = AstarMath.IntToColor(i, 0.5F);
        for (int j = 0; j < path.Count-1; j++) {
            // Plot segment j to j+1 with a nice color got from Pathfinding.AstarMath.IntToColor
            Debug.DrawLine(path[j], path[j+1], color, 10);
        }
    }
}

}
"

I’m new in C# programming and I would appreciate so much every kind of help in order to solve my problem.
Thank you very much in advance guys!


#2

Hi

This should work I think.
You should not use the AIDestinationSetter together with this script, as that would override this script.

class Blah : MonoBehaviour {
	public Transform[] targets;

	void Search () {
		// Set the target points
		Vector3[] endPoints = new Vector3[targets.Length];
		for (int i = 0; i < targets.Length; i++) endPoints[i] = targets[i].position;

		MultiTargetPath p = MultiTargetPath.Construct(transform.position, endPoints, null, null);
		// Find a path only to the closest one, not necessarily all of them
		p.pathsForAll = false;

		// This is either AIPath, RichAI or AILerp depending on which movement script you are using.
		// All of them implement this interface
		var ai = GetComponent<IAstarAI>();
		// Disable the built-in path recalculation of the movement script
		ai.canSearch = false;
		// Use the MultiTargetPath instead (the movement script will send it to the Seeker to be calculated)
		ai.SetPath(p);
	}
}

#3

Hi Aron,
thank you for your fast answer and congratulations for this excellent Unity asset!
I’ve added the script you attached to my player along with Seeker and AI Path components but unfortunately it doesn’t work. In other words no path generated (no green line) and the player is motionless. What’s going wrong?
Anyway this is the script:
"
using UnityEngine;
using Pathfinding;
using System.Collections.Generic;

public class test : MonoBehaviour
{
public Transform[] targets;

void Search()
{
    // Set the target points
    Vector3[] endPoints = new Vector3[targets.Length];
    for (int i = 0; i < targets.Length; i++) endPoints[i] = targets[i].position;

    MultiTargetPath p = MultiTargetPath.Construct(transform.position, endPoints, null, null);
    // Find a path only to the closest one, not necessarily all of them
    p.pathsForAll = false;

    // This is either AIPath, RichAI or AILerp depending on which movement script you are using.
    // All of them implement this interface
    var ai = GetComponent<IAstarAI>();
    // Disable the built-in path recalculation of the movement script
    ai.canSearch = false;
    // Use the MultiTargetPath instead (the movement script will send it to the Seeker to be calculated)
    ai.SetPath(p);
}

}
"
Thank you!


#4

I just made a method there, but you have to actually call it when you want to recalculate the path. You could for example call it from a Start method (which is called when the game starts).


#5

using UnityEngine;
using Pathfinding;
using System.Collections.Generic;

public class Test : MonoBehaviour
{
public Transform[] targets;

void Start()
{
    Search();
}

void Search()
{
    // Set the target points
    Vector3[] endPoints = new Vector3[targets.Length];
    for (int i = 0; i < targets.Length; i++) endPoints[i] = targets[i].position;

    MultiTargetPath p = MultiTargetPath.Construct(transform.position, endPoints, null, null);
    // Find a path only to the closest one, not necessarily all of them
    p.pathsForAll = false;

    // This is either AIPath, RichAI or AILerp depending on which movement script you are using.
    // All of them implement this interface
    var ai = GetComponent<IAstarAI>();
    // Disable the built-in path recalculation of the movement script
    ai.canSearch = false;
    // Use the MultiTargetPath instead (the movement script will send it to the Seeker to be calculated)
    ai.SetPath(p);
}

}
Ok, really good now it’s working, thank you very much!

I have a few questions:
1 - If my targets are moving, how can I update the search process? In other words I’d like that my player follow a target or change it if another one come closer. I’ve tried with the function “Update()” or setting “true” in the “ai.canSearch”, but I wrong something. It doesn’t work.
2 - I’d like to set as multiple targets all the children of a game object and find at the same way the closest. What I should change?

Thank you again and again!


#6

Ok, I’ve solved the first question simply calling the “Search” method inside the “Update” method. Now my player is able to find the closest path between multi-targets in real time. I’m sorry for my question, it was so easy.


#7

Hi

Ok. But you really shouldn’t be doing that since path requests are asynchronous.
You could do something like

if (seeker.IsDone()) Search();

however.

See https://arongranberg.com/astar/docs/callingpathfinding.html


#8

Ok, thank you Aron, I will have a look!
I’d really appreciate a help about another problem that is driving me crazy.
I have an empty gameobject “Targets” (father) that contains children gameobject called “Target1”, “Target2”, “Target3” and so on until “Target100”.
How could I set the multi-target simply dragging and dropping the father gameobject “Targets”?
It could be really useful considering that it would be too long to set each target singularly.
Thank you in advance!


#9

Hi

That code was part of the original script that you posted.
I would also suggest that you read up a bit on scripting in unity in general. See for example: https://docs.unity3d.com/ScriptReference/Transform.html