Path into range of target

Hi everyone,

What I’ve been trying to do is to get a ranged unit to find a path that will get it within range of a target. The obvious way is to simply path to the target, and periodically check the distance, and when the distance is less than the unit’s range, to start attacking it. This works fine, but is not optimal in a situation where there is something between the unit and the target blocking the navmesh, but which the unit could fire over. Consider this crude sketch:

The red dot has a range indicated by the pink circle, and wants to attack the green dot. The blue area is water that the red dot cannot cross, but can fire over. Using the above method, the red dot would follow the black path to get into range of the green dot. What I’m looking for is a way for the red dot to realize that it can get into range of the green dot by simply moving forward a bit (the pink path), since a position on the near side of the water would put it into range of the green dot without having to go all the way around the water.

Is there a way using A* pathfinding project to get the seeker on the red dot to find the pink path rather than the black one?

Hi

You can use the XPath path type together with the EndingConditionProximity class.
Essentially you would do something like this:

var path = XPath.Construct(startPoint, endPoint);
var maxDistance = 100f;
path.endingCondition = new EndingConditionProximity(path, maxDistance);
seeker.StartPath(path, OnPathComplete);

(I haven’t tested the above code, but I think it should work. You will either have to use a custom movement script or modify one of the existing ones however).

See https://arongranberg.com/astar/docs/class_pathfinding_1_1_x_path.php
See https://arongranberg.com/astar/docs/class_pathfinding_1_1_ending_condition_proximity.php

Thanks Aron, that’s just what I’m after

I have the same problem as RonaldMcScotland. However I’m using a Recast graph. In the generated graph the nodes are far apart from each other.

I’m using a custom class that inherits from ABPathEndingCondition in conjunction with XPath to find suitable attack positions for units. The method “public virtual bool TargetFound(PathNode node)” of ABPathEndingCondition is called for each node. If the intended target is not close enough to a node, no path will be found.

Any help will be greatly appreciated.

Hi

@Angstr0m
For a recast graph, this approach will not work well because there may be no nodes within that range at all.
Instead I would recommend that you request a path to the target point as normal, but then make the agent stop once it is within range. If you are using the AIPath script you can do this by setting the ‘end reached distance’ field to a high value and making sure that the ‘when close to destination’ is set to ‘Stop’.

Alternatively you could use the TriangleMeshNode.ClosestPointOnNode method inside the TargetFound method (you would have to create a custom ending condition class) to get a more accurate distance measurement.

Thanks for the reply.

The first alternative is relatively close to the current implementation. Instead of using the configuration of the agent, the distance checking is done inside the behavior tree of the unit. This does not yield optimal results in every situation but works well enough.

The ClosestPointOnNode inside the TargetFound method looks very promising. I will look into it. Thanks for your advice.

One further question:
As an alternative approach I changed the pathfinding to use a grid-grap instead of the recast graph. The problem with this approach is that I’m using off-mesh. Is there an agent type that supports grid-graphs and off-mesh links? RichAI only seems to work on mesh-based graphs.

Currently no included agent supports off mesh links on grid graphs. They will follow them, but they just assume it’s a normal path, they don’t do anything special for off-mesh links.

Thanks again.

Imho having such an agent would be nice. If it isn’t already on the roadmap, I would like to propose this feature for a future version.

Yeah… I have actually attempted to implement one a few times. But it’s really hard to do while keeping backwards compatibility and making a good API… but oh well, at some point I will find a good solution I hope.