160 lines
5.4 KiB
C#
160 lines
5.4 KiB
C#
using UnityEngine;
|
|
using Pathfinding.Pooling;
|
|
|
|
namespace Pathfinding {
|
|
/// <summary>
|
|
/// Restrict suitable nodes by if they have been searched by a FloodPath.
|
|
///
|
|
/// Suitable nodes are in addition to the basic contraints, only the nodes which return true on a FloodPath.HasPathTo (node) call.
|
|
/// See: Pathfinding.FloodPath
|
|
/// See: Pathfinding.FloodPathTracer
|
|
/// </summary>
|
|
public class FloodPathConstraint : NNConstraint {
|
|
readonly FloodPath path;
|
|
|
|
public FloodPathConstraint (FloodPath path) {
|
|
if (path == null) { Debug.LogWarning("FloodPathConstraint should not be used with a NULL path"); }
|
|
this.path = path;
|
|
}
|
|
|
|
public override bool Suitable (GraphNode node) {
|
|
return base.Suitable(node) && path.HasPathTo(node);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Traces a path created with the Pathfinding.FloodPath.
|
|
///
|
|
/// See Pathfinding.FloodPath for examples on how to use this path type
|
|
///
|
|
/// [Open online documentation to see images]
|
|
/// </summary>
|
|
public class FloodPathTracer : ABPath {
|
|
/// <summary>Reference to the FloodPath which searched the path originally</summary>
|
|
protected FloodPath flood;
|
|
|
|
protected override bool hasEndPoint => false;
|
|
|
|
/// <summary>
|
|
/// Default constructor.
|
|
/// Do not use this. Instead use the static Construct method which can handle path pooling.
|
|
/// </summary>
|
|
public FloodPathTracer () {}
|
|
|
|
public static FloodPathTracer Construct (Vector3 start, FloodPath flood, OnPathDelegate callback = null) {
|
|
var p = PathPool.GetPath<FloodPathTracer>();
|
|
|
|
p.Setup(start, flood, callback);
|
|
return p;
|
|
}
|
|
|
|
protected void Setup (Vector3 start, FloodPath flood, OnPathDelegate callback) {
|
|
this.flood = flood;
|
|
|
|
if (flood == null || flood.PipelineState < PathState.Returning) {
|
|
throw new System.ArgumentException("You must supply a calculated FloodPath to the 'flood' argument");
|
|
}
|
|
|
|
base.Setup(start, flood.originalStartPoint, callback);
|
|
nnConstraint = new FloodPathConstraint(flood);
|
|
}
|
|
|
|
protected override void Reset () {
|
|
base.Reset();
|
|
flood = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the path.
|
|
/// Traces the path from the start node.
|
|
/// </summary>
|
|
protected override void Prepare () {
|
|
if (!this.flood.IsValid(pathHandler.nodeStorage)) {
|
|
FailWithError("The flood path is invalid because nodes have been destroyed since it was calculated. Please recalculate the flood path.");
|
|
return;
|
|
}
|
|
|
|
base.Prepare();
|
|
|
|
if (CompleteState == PathCompleteState.NotCalculated) {
|
|
for (uint i = 0; i < pathHandler.numTemporaryNodes; i++) {
|
|
var nodeIndex = pathHandler.temporaryNodeStartIndex + i;
|
|
ref var tempNode = ref pathHandler.GetTemporaryNode(nodeIndex);
|
|
if (tempNode.type == TemporaryNodeType.Start) {
|
|
var node = pathHandler.GetNode(tempNode.associatedNode);
|
|
|
|
// This is guaranteed by the FloodPathConstraint
|
|
bool found = false;
|
|
for (uint k = 0; k < node.PathNodeVariants; k++) {
|
|
if (flood.GetParent(node.NodeIndex + k) != 0) {
|
|
found = true;
|
|
CompleteState = PathCompleteState.Complete;
|
|
Trace(node.NodeIndex + k);
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
FailWithError("The flood path did not contain any information about the end node. Have you modified the path's nnConstraint to an instance which does not subclass FloodPathConstraint?");
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
FailWithError("Could not find a valid start node");
|
|
}
|
|
}
|
|
|
|
protected override void CalculateStep (long targetTick) {
|
|
if (CompleteState != PathCompleteState.Complete) throw new System.Exception("Something went wrong. At this point the path should be completed");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Traces the calculated path from the start node to the end.
|
|
/// This will build an array (<see cref="path)"/> of the nodes this path will pass through and also set the <see cref="vectorPath"/> array to the <see cref="path"/> arrays positions.
|
|
/// This implementation will use the <see cref="flood"/> (FloodPath) to trace the path from precalculated data.
|
|
/// </summary>
|
|
protected override void Trace (uint fromPathNodeIndex) {
|
|
uint pathNodeIndex = fromPathNodeIndex;
|
|
int count = 0;
|
|
GraphNode lastNode = null;
|
|
|
|
while (pathNodeIndex != 0) {
|
|
if ((pathNodeIndex & FloodPath.TemporaryNodeBit) != 0) {
|
|
// Skip over temporary nodes
|
|
pathNodeIndex = flood.GetParent(pathNodeIndex & ~FloodPath.TemporaryNodeBit);
|
|
} else {
|
|
var node = pathHandler.GetNode(pathNodeIndex);
|
|
if (node == null) {
|
|
FailWithError("A node in the path has been destroyed. The FloodPath needs to be recalculated before you can use a FloodPathTracer.");
|
|
return;
|
|
}
|
|
|
|
// If a node has multiple variants (like the triangle mesh node), then we may visit
|
|
// the same node multiple times in a sequence (but different variants of it).
|
|
// In the final path we don't want the duplicates.
|
|
if (node != lastNode) {
|
|
if (!CanTraverse(node)) {
|
|
FailWithError("A node in the path is no longer walkable. The FloodPath needs to be recalculated before you can use a FloodPathTracer.");
|
|
return;
|
|
}
|
|
path.Add(node);
|
|
lastNode = node;
|
|
vectorPath.Add((Vector3)node.position);
|
|
}
|
|
var next = flood.GetParent(pathNodeIndex);
|
|
if (next == pathNodeIndex) {
|
|
break;
|
|
}
|
|
pathNodeIndex = next;
|
|
}
|
|
|
|
count++;
|
|
if (count > 10000) {
|
|
Debug.LogWarning("Infinite loop? >10000 node path. Remove this message if you really have that long paths");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|