Support Forum

Return an object when finding closest position

I’ve got a list of constantly moving GameObjects that each need to determine the closest path to an object in another list of constantly moving GameObjects. When an object needs to select a new target I’m converting one of the GameObject lists into an array of Vector 3 positions and feeding it into MultiTargetPath. The problem is that MultiTargetPath sends me to a Vector3 coordinate when what I really need it to do is lock me onto the closest object.

I would love to figure out how to use MultiTargetPath.Construct to determine the closest object without starting the path, then assigning that GameObject to the Target for AIDestinationSetter. Because all of the objects are in motion a Vector3 coordinate isn’t useful seconds after I’ve received it—I need to be able to lock onto the closest object.

I’ve tried to pick apart the callbacks in MultiTargetPath.Construct and StartMultiPath to compare the endpoint of the shortest path (which I think is the first path in the list?) with the position of the object in the list and use that comparison to determine the GameObject to target but it feels clunky and prone to error—there’s got to be a better way.

In an ideal world, I would be able to give MultiTargetPath a list (or an array) of objects, have it determine which one is the closest, and return that object for me to use elsewhere (ie: AIDestinationSetter). I’d appreciate any guidance in how I could get there without altering any code in Seeker.cs :see_no_evil: or another approach for this functionality.

Hi

You can do something like this:

Vector3[] myListOfTargets = ...;
var path = MultiTargetPath.Construct(transform.position, myListOfTargets, null);
// Calculate the path using the AstarPath object directly instead of using a seeker
// to avoid sending the path to the movement scripts
AstarPath.StartPath(path);
// For simplicity in this example, don't calculate the path asynchronously
path.BlockUntilCalculated();
// Will be -1 if no target was found, otherwise the index in the myListOfTargets array that was the closest
var closestObjectIndex = path.chosenTarget;
1 Like

This is a much more efficient way to do what I was trying to do, thanks!

1 Like

At first glance, this seemed to be working fine but then in practice, I noticed something weird that I can’t figure out. Here’s my version of that code you posted:

    public void SeekNewOpening()
    {
        targetedOpening = _core.pathing.GetClosestOpening(_vacantOpponentOpenings);
        aiDestinationSetter.target = targetedOpening.transform;
    }


    public UnitOpening GetClosestOpening(List<UnitOpening> opponentOpenings)
    {
        Vector3[] endPoints = new Vector3[opponentOpenings.Count];

        for (int i = 0; i < endPoints.Length; i++)
        {
            endPoints[i] = opponentOpenings[i].transform.position;
        }

        var path = MultiTargetPath.Construct(transform.position, endPoints, null);
        AstarPath.StartPath(path);
        path.BlockUntilCalculated();
        var closestOpeningIndex = path.chosenTarget;
        return opponentOpenings[closestOpeningIndex];
        }

In this screenshot, all of the units are using this script but for some reason are all targeting the right opening when the left one is clearly closer to the two units on the left. I double-checked and both openings are being considered when running GetClosestOpening.

I’ve tried a few things but have no idea how to debug this so any guidance would be greatly appreciated!

Hi

Are you using a recast graph or a grid graph?

It’s using a recast graph.