That would have to be done by you in your OnPathComplete function. Or I guess in a custom path modifier (but that’s more complex). Iterate over all points in the path.vectorPath
list and add a constant offset to each of the points.
Ok, correct me if I’m wrong: I can leave the implementation as it is in the example
And then I change offset like on second screenshot?
Yes. That looks perfectly correct.
Then you just need to offset the path by nodeSize * (width/2, height/2)
Thank you, seems like it’s working
But what if I want the object to fit exactly on the grid squares. Something like that:
Again, sorry for annoying)
Right, I think my offset that I mentioned was incorrect. It should be:
nodeSize * ((width - 1)/2, (height - 1)/2)
Thank you for reply. But nothing really changes(Sorry can’t load more than 2 image, but it’s same as in a previous reply)
I show you again my code. I think problem in ITraversalProvider realization.
You are creating your SquareShape with a constant size of 2. But your _width
and _height
fields may not be 2. Are they also 2 or are they set to different values?
_width and _height are also set to 2. I trying to make movement for 2x2 size objects
That’s odd. In that case, the offset should be 0.5 nodes in each direction. So the object should end up between nodes, not at the center of nodes.
Yeah, I get it. But how I can do this)? If I do it like on picture, nothing changes
Hmm. That script doesn’t move the agent. How do you move the actual agent?
If you are using a movement script, you probably want to send the path to the movement script after you have modified the path:
void SearchPath() {
...
// Note: use AstarPath.SearchPath here instead to avoid the movement script picking it up automatically
AstarPath.SearchPath(path);
}
void OnPathComplete(Path path) {
... modify the path here
path.originalStartPoint += offset;
path.originalEndPoint += offset;
// Apply any path modifiers
seeker.PostProcess(path);
// Send the path to the agent so that it can follow it
_ai.SetPath(path);
}
My code look like that:
If I change it to that, my object doesn’t move anymore(and I don’t have SearchPath() method in AstarPath class
Sorry. AstarPath.StartPath
. You should call that after you set the traversal provider.
ABPath path = ...
path.traversalProvider = ...
AstarPath.StartPath(path);
Hmmm, my code now looks like this:
But it’s still doesn’t move my object. Am I doing everything right?
The _ai.SetPath
call should be at the end of OnPathComplete, like in my previous code snippet.
I trying like you said, but I got exception
Exception text:
ArgumentException: You must call the SetPath method with a path that either has been completely calculated or one whose path calculation has not been started at all. It looks like the path calculation for the path you tried to use has been started, but is not yet finished.
Then I trying like this, and I got new several exceptions
Exceptions:
ArgumentException: You must call the SetPath method with a path that either has been completely calculated or one whose path calculation has not been started at all. It looks like the path calculation for the path you tried to use has been started, but is not yet finished.
Unhandled exception during pathfinding. Terminating.
UnityEngine.Debug:LogError (object)
Pathfinding.PathProcessor:CalculatePathsThreaded (Pathfinding.PathHandler) (at Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs:408)
Pathfinding.PathProcessor/<>c__DisplayClass24_0:<.ctor>b__0 () (at Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs:110)
System.Threading.ThreadHelper:ThreadStart ()
Error : This part should never be reached.
UnityEngine.Debug:LogError (object)
Look how I modified my code. Now it’s works without exception, but still I can’t setup offsets for 2x2 objects.
By the way, if I use AstarPath.StartPath and his callback, that doesn’t work and throw exceptions
Hmmm. Right. I think you are encountering an issue in the currently released version in which calling ai.SetPath from an OnPathComplete callback is not allowed. I have fixed this in the beta, but I haven’t backported the fix yet.
Your new code will not work because you are trying to modify the vectorPath before the path has been calculated. At that time the vectorPath will always have a length of zero. You need to offset it in the OnPathComplete method.
Sorry for all the trouble.
No problem. I offset modifying of vector path in the OnPathComplete. But it’s still not desired behaviour
Here’s a script that I have verified works:
using UnityEngine;
using Pathfinding;
using Pathfinding.Drawing;
public class GridShapeTraversalProviderTest : VersionedMonoBehaviour {
IAstarAI ai;
Seeker seeker;
public Transform target;
public int width;
void Awake () {
ai = GetComponent<IAstarAI>();
seeker = GetComponent<Seeker>();
}
class GridShapeTraversalProvider : ITraversalProvider {
Int2[] shape;
/** Create a GridShapeTraversalProvider using a square shape.
* The width parameter must be an odd number.
*/
public static GridShapeTraversalProvider SquareShape (int width) {
var shape = new GridShapeTraversalProvider();
shape.shape = new Int2[width*width];
// Create an array containing all integer points within a width*width square
int i = 0;
for (int x = 0; x < width; x++) {
for (int z = 0; z < width; z++) {
shape.shape[i] = new Int2(x, z);
i++;
}
}
return shape;
}
public bool CanTraverse (Path path, GraphNode node) {
return DefaultITraversalProvider.CanTraverse(path, node);
}
public int OverlappingNodes (Path path, GraphNode node) {
GridNodeBase gridNode = node as GridNodeBase;
// Don't do anything special for non-grid nodes
if (gridNode == null) return 0;
int x0 = gridNode.XCoordinateInGrid;
int z0 = gridNode.ZCoordinateInGrid;
var grid = gridNode.Graph as GridGraph;
// Iterate through all the nodes in the shape around the current node
// and check if those nodes are also traversable.
int count = 0;
for (int i = 0; i < shape.Length; i++) {
var inShapeNode = grid.GetNode(x0 + shape[i].x, z0 + shape[i].y);
if (inShapeNode == null || !DefaultITraversalProvider.CanTraverse(path, inShapeNode)) count++;
}
return count;
}
public bool CanTraverse (Path path, GraphNode from, GraphNode to) {
return CanTraverse(path, to);
}
public uint GetTraversalCost (Path path, GraphNode node) {
// Use the default traversal cost.
// Optionally this could be modified to e.g taking the average of the costs inside the shape.
return DefaultITraversalProvider.GetTraversalCost(path, node) + (uint)OverlappingNodes(path, node) * 10000;
}
// This can be omitted in Unity 2021.3 and newer because a default implementation (returning true) can be used there.
public bool filterDiagonalGridConnections {
get {
return true;
}
}
}
Vector3 centerOffset() {
var gg = AstarPath.active.data.gridGraph;
return gg.nodeSize * new Vector3((width-1)*0.5f, 0, (width-1)*0.5f);
}
void SearchPath() {
// Note: use AstarPath.SearchPath here instead to avoid the movement script picking it up automatically
var path = ABPath.Construct(transform.position - centerOffset(), target.position - centerOffset(), OnPathComplete);
path.traversalProvider = GridShapeTraversalProvider.SquareShape(width);
AstarPath.StartPath(path);
}
void OnPathComplete(Path _path) {
var path = _path as ABPath;
if (path.error) {
Debug.LogError(path.errorLog);
return;
}
var offset = centerOffset();
for (int i = 0; i < path.vectorPath.Count; i++) {
path.vectorPath[i] += offset;
}
path.originalStartPoint += offset;
path.originalEndPoint += offset;
path.startPoint += offset;
path.endPoint += offset;
// Apply any path modifiers
seeker.PostProcess(path);
// Send the path to the agent so that it can follow it
ai.SetPath(path);
using(Draw.WithDuration(5.0f)) {
Draw.Polyline(path.vectorPath, Color.green);
}
}
void Update () {
if ((Time.frameCount % 60) == 0) {
SearchPath();
}
}
}
I changed it to apply a large penalty to nodes instead of making them completely unwalkable. That will allow it to recover if it happens to move too close to a wall, instead of the path just failing.