PathHandler.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. #define DECREASE_KEY
  2. using System.Collections.Generic;
  3. namespace Pathfinding {
  4. /// <summary>
  5. /// Stores temporary node data for a single pathfinding request.
  6. /// Every node has one PathNode per thread used.
  7. /// It stores e.g G score, H score and other temporary variables needed
  8. /// for path calculation, but which are not part of the graph structure.
  9. ///
  10. /// See: Pathfinding.PathHandler
  11. /// See: https://en.wikipedia.org/wiki/A*_search_algorithm
  12. /// </summary>
  13. public class PathNode {
  14. /// <summary>Reference to the actual graph node</summary>
  15. public GraphNode node;
  16. /// <summary>Parent node in the search tree</summary>
  17. public PathNode parent;
  18. /// <summary>The path request (in this thread, if multithreading is used) which last used this node</summary>
  19. public ushort pathID;
  20. #if DECREASE_KEY
  21. /// <summary>
  22. /// Index of the node in the binary heap.
  23. /// The open list in the A* algorithm is backed by a binary heap.
  24. /// To support fast 'decrease key' operations, the index of the node
  25. /// is saved here.
  26. /// </summary>
  27. public ushort heapIndex = BinaryHeap.NotInHeap;
  28. #endif
  29. /// <summary>Bitpacked variable which stores several fields</summary>
  30. private uint flags;
  31. /// <summary>Cost uses the first 28 bits</summary>
  32. private const uint CostMask = (1U << 28) - 1U;
  33. /// <summary>Flag 1 is at bit 28</summary>
  34. private const int Flag1Offset = 28;
  35. private const uint Flag1Mask = (uint)(1 << Flag1Offset);
  36. /// <summary>Flag 2 is at bit 29</summary>
  37. private const int Flag2Offset = 29;
  38. private const uint Flag2Mask = (uint)(1 << Flag2Offset);
  39. public uint cost {
  40. get {
  41. return flags & CostMask;
  42. }
  43. set {
  44. flags = (flags & ~CostMask) | value;
  45. }
  46. }
  47. /// <summary>
  48. /// Use as temporary flag during pathfinding.
  49. /// Pathfinders (only) can use this during pathfinding to mark
  50. /// nodes. When done, this flag should be reverted to its default state (false) to
  51. /// avoid messing up other pathfinding requests.
  52. /// </summary>
  53. public bool flag1 {
  54. get {
  55. return (flags & Flag1Mask) != 0;
  56. }
  57. set {
  58. flags = (flags & ~Flag1Mask) | (value ? Flag1Mask : 0U);
  59. }
  60. }
  61. /// <summary>
  62. /// Use as temporary flag during pathfinding.
  63. /// Pathfinders (only) can use this during pathfinding to mark
  64. /// nodes. When done, this flag should be reverted to its default state (false) to
  65. /// avoid messing up other pathfinding requests.
  66. /// </summary>
  67. public bool flag2 {
  68. get {
  69. return (flags & Flag2Mask) != 0;
  70. }
  71. set {
  72. flags = (flags & ~Flag2Mask) | (value ? Flag2Mask : 0U);
  73. }
  74. }
  75. /// <summary>Backing field for the G score</summary>
  76. private uint g;
  77. /// <summary>Backing field for the H score</summary>
  78. private uint h;
  79. /// <summary>G score, cost to get to this node</summary>
  80. public uint G { get { return g; } set { g = value; } }
  81. /// <summary>H score, estimated cost to get to to the target</summary>
  82. public uint H { get { return h; } set { h = value; } }
  83. /// <summary>F score. H score + G score</summary>
  84. public uint F { get { return g+h; } }
  85. public void UpdateG (Path path) {
  86. #if ASTAR_NO_TRAVERSAL_COST
  87. g = parent.g + cost;
  88. #else
  89. g = parent.g + cost + path.GetTraversalCost(node);
  90. #endif
  91. }
  92. }
  93. /// <summary>Handles thread specific path data.</summary>
  94. public class PathHandler {
  95. /// <summary>
  96. /// Current PathID.
  97. /// See: <see cref="PathID"/>
  98. /// </summary>
  99. private ushort pathID;
  100. public readonly int threadID;
  101. public readonly int totalThreadCount;
  102. /// <summary>
  103. /// Binary heap to keep track of nodes on the "Open list".
  104. /// See: https://en.wikipedia.org/wiki/A*_search_algorithm
  105. /// </summary>
  106. public readonly BinaryHeap heap = new BinaryHeap(128);
  107. /// <summary>ID for the path currently being calculated or last path that was calculated</summary>
  108. public ushort PathID { get { return pathID; } }
  109. /// <summary>Array of all PathNodes</summary>
  110. public PathNode[] nodes = new PathNode[0];
  111. /// <summary>
  112. /// StringBuilder that paths can use to build debug strings.
  113. /// Better for performance and memory usage to use a single StringBuilder instead of each path creating its own
  114. /// </summary>
  115. public readonly System.Text.StringBuilder DebugStringBuilder = new System.Text.StringBuilder();
  116. public PathHandler (int threadID, int totalThreadCount) {
  117. this.threadID = threadID;
  118. this.totalThreadCount = totalThreadCount;
  119. }
  120. public void InitializeForPath (Path p) {
  121. pathID = p.pathID;
  122. heap.Clear();
  123. }
  124. /// <summary>Internal method to clean up node data</summary>
  125. public void DestroyNode (GraphNode node) {
  126. PathNode pn = GetPathNode(node);
  127. // Clean up references to help the GC
  128. pn.node = null;
  129. pn.parent = null;
  130. // This is not required for pathfinding, but not clearing it may confuse gizmo drawing for a fraction of a second.
  131. // Especially when 'Show Search Tree' is enabled
  132. pn.pathID = 0;
  133. pn.G = 0;
  134. pn.H = 0;
  135. }
  136. /// <summary>Internal method to initialize node data</summary>
  137. public void InitializeNode (GraphNode node) {
  138. //Get the index of the node
  139. int ind = node.NodeIndex;
  140. if (ind >= nodes.Length) {
  141. // Grow by a factor of 2
  142. PathNode[] newNodes = new PathNode[System.Math.Max(128, nodes.Length*2)];
  143. nodes.CopyTo(newNodes, 0);
  144. // Initialize all PathNode instances at once
  145. // It is important that we do this here and don't for example leave the entries as NULL and initialize
  146. // them lazily. By allocating them all at once we are much more likely to allocate the PathNodes close
  147. // to each other in memory (most systems use some kind of bumb-allocator) and this improves cache locality
  148. // and reduces false sharing (which would happen if we allocated PathNodes for the different threads close
  149. // to each other). This has been profiled to give around a 4% difference in overall pathfinding performance.
  150. for (int i = nodes.Length; i < newNodes.Length; i++) newNodes[i] = new PathNode();
  151. nodes = newNodes;
  152. }
  153. nodes[ind].node = node;
  154. }
  155. public PathNode GetPathNode (int nodeIndex) {
  156. return nodes[nodeIndex];
  157. }
  158. /// <summary>
  159. /// Returns the PathNode corresponding to the specified node.
  160. /// The PathNode is specific to this PathHandler since multiple PathHandlers
  161. /// are used at the same time if multithreading is enabled.
  162. /// </summary>
  163. public PathNode GetPathNode (GraphNode node) {
  164. return nodes[node.NodeIndex];
  165. }
  166. /// <summary>
  167. /// Set all nodes' pathIDs to 0.
  168. /// See: Pathfinding.PathNode.pathID
  169. /// </summary>
  170. public void ClearPathIDs () {
  171. for (int i = 0; i < nodes.Length; i++) {
  172. if (nodes[i] != null) nodes[i].pathID = 0;
  173. }
  174. }
  175. }
  176. }