summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Pathfinders/FloodPathTracer.cs
blob: b743983529f08ef5f6bafcdcda1d33f1129a8b08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using UnityEngine;

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;

			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 (!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);
					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;
				}
			}
		}
	}
}