PathHandler.cs 6.0 KB

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