Support Forum

Extending new path types

Hi, I’m using RecastGraph and I’d like to extend a slightly more specific path type from ABPath.
I want to give more weight under certain conditions when searching for path.
This requires overriding the Open(function) of TriangleMeshNode.
So I copied the Open function from TriangleMeshNode to the new Path Type with hardcoding and added a line of code that I needed.
But when I installed the Astar with Unity package, it became impossible.
What approach should I take to solve this problem?
Whenever a new version is released, it would be too cumbersome and problematic to copy and install the package again.

Finally, I attach the New Path that I made.
Thank you.

public interface IAuthTraversalProvider
{
	bool IsAuthed (GraphNode start, GraphNode end);
}

public class AuthedPath : XPath
{
	public AuthedPath () { }

	public IAuthTraversalProvider authTraversalProvider;

	public new static AuthedPath Construct (Vector3 start, Vector3 end, OnPathDelegate callback = null)
	{
		var p = PathPool.GetPath<AuthedPath> ();

		p.Setup (start, end, callback);
		p.endingCondition = new ABPathEndingCondition (p);
		return p;
	}

	protected override void CalculateStep (long targetTick) {
		int counter = 0;

		// Continue to search as long as we haven't encountered an error and we haven't found the target
		while (CompleteState == PathCompleteState.NotCalculated) {
			searchedNodes++;

			// Close the current node, if the current node is the target node then the path is finished
			if (endingCondition.TargetFound(currentR)) {
				CompleteState = PathCompleteState.Complete;
				break;
			}

			// Loop through all walkable neighbours of the node and add them to the open list.
			Open(currentR.node as MeshNode, this, currentR, pathHandler);

			// Any nodes left to search?
			if (pathHandler.heap.isEmpty) {
				FailWithError("Searched whole area but could not find target");
				return;
			}

			// Select the node with the lowest F score and remove it from the open list
			currentR = pathHandler.heap.Remove();

			// Check for time every 500 nodes, roughly every 0.5 ms usually
			if (counter > 500) {
				// Have we exceded the maxFrameTime, if so we should wait one frame before continuing the search since we don't want the game to lag
				if (System.DateTime.UtcNow.Ticks >= targetTick) {
					//Return instead of yield'ing, a separate function handles the yield (CalculatePaths)
					return;
				}
				counter = 0;

				if (searchedNodes > 1000000) {
					throw new System.Exception("Probable infinite loop. Over 1,000,000 nodes searched");
				}
			}

			counter++;
		}

		if (CompleteState == PathCompleteState.Complete) {
			ChangeEndNode(currentR.node);
			Trace(currentR);
		}
	}

	public void Open (MeshNode meshNode, Path path, PathNode pathNode, PathHandler handler)
	{
		if (meshNode.connections == null) return;

		// Flag2 indicates if this node needs special treatment
		// with regard to connection costs
		bool flag2 = pathNode.flag2;

		// Loop through all connections
		for (int i = meshNode.connections.Length - 1; i >= 0; i--)
		{
			var conn = meshNode.connections[i];
			var other = conn.node;

			// Make sure we can traverse the neighbour
			if (path.CanTraverse (conn.node))
			{
				PathNode pathOther = handler.GetPathNode (conn.node);

				// Fast path out, worth it for triangle mesh nodes since they usually have degree 2 or 3
				if (pathOther == pathNode.parent)
				{
					continue;
				}

				if (!authTraversalProvider.IsAuthed (meshNode, conn.node))
					continue;

				uint cost = conn.cost;

				if (flag2 || pathOther.flag2)
				{
					// Get special connection cost from the path
					// This is used by the start and end nodes
					cost = path.GetConnectionSpecialCost (meshNode, conn.node, cost);
				}

				// Test if we have seen the other node before
				if (pathOther.pathID != handler.PathID)
				{
					// We have not seen the other node before
					// So the path from the start through this node to the other node
					// must be the shortest one so far

					// Might not be assigned
					pathOther.node = conn.node;

					pathOther.parent = pathNode;
					pathOther.pathID = handler.PathID;

					pathOther.cost = cost;

					pathOther.H = path.CalculateHScore (other);
					pathOther.UpdateG (path);

					handler.heap.Add (pathOther);
				}
				else
				{
					// If not we can test if the path from this node to the other one is a better one than the one already used
					if (pathNode.G + cost + path.GetTraversalCost (other) < pathOther.G)
					{
						pathOther.cost = cost;
						pathOther.parent = pathNode;

						other.UpdateRecursiveG (path, pathOther, handler);
					}
				}
			}
		}
	}

	/// <summary>
	/// Changes the <see cref="endNode"/> to target and resets some temporary flags on the previous node.
	/// Also sets <see cref="endPoint"/> to the position of target.
	/// </summary>
	void ChangeEndNode (GraphNode target)
	{
		// Reset temporary flags on the previous end node, otherwise they might be
		// left in the graph and cause other paths to calculate paths incorrectly
		if (endNode != null && endNode != startNode)
		{
			var pathNode = pathHandler.GetPathNode (endNode);
			pathNode.flag1 = pathNode.flag2 = false;
		}

		endNode = target;
		endPoint = (Vector3)target.position;
	}


}

(Psuedo version)

public interface IAuthTraversalProvider
{
	bool IsAuthed (GraphNode start, GraphNode end);
}

public class AuthedPath : XPath
{
	public AuthedPath () { }

	public IAuthTraversalProvider authTraversalProvider;

	public new static AuthedPath Construct (Vector3 start, Vector3 end, OnPathDelegate callback = null)
	{
		~~~~~~~
	}

	protected override void CalculateStep (long targetTick) {
		while (CompleteState == PathCompleteState.NotCalculated) {
			searchedNodes++;

			// Continue to search as long as we haven't encountered an error and we haven't found the target

			// I want to override this method..!!
			Open(currentR.node as MeshNode, this, currentR, pathHandler);

			// Any nodes left to search?

			// Select the node with the lowest F score and remove it from the open list

			// Check for time every 500 nodes, roughly every 0.5 ms usually
		}

		if (CompleteState == PathCompleteState.Complete) {
			ChangeEndNode(currentR.node);
			Trace(currentR);
		}
	}

	public void Open (MeshNode meshNode, Path path, PathNode pathNode, PathHandler handler)
	{
		// codes for TriangleMeshNode

		// Loop through all connections
		for (int i = meshNode.connections.Length - 1; i >= 0; i--)
		{
			var conn = meshNode.connections[i];
			var other = conn.node;

			// Make sure we can traverse the neighbour
			if (path.CanTraverse (conn.node))
			{
				~~~~~~~~

				// added condition for check!!
				if (!authTraversalProvider.IsAuthed (meshNode, conn.node))
					continue;

				~~~~~~~~
			}
		}
	}

}

Hi

I think you should be able to do all this outside of the pathfinding package. You add what you wrote to a new script outside the package. Does this not work?

No, it doesn’t
Error occurs due to internal keyword properties(and functions).

If it’s impossible to solve in my approach, what are some ways to override the evaluating node-cost algorithm?



I added my code outside the package and it didn’t work. Aron.
Because of the props with internal reserved keyword.

How can I override the evaluating node-cost algorithm? Is there any other way?