PathFindHelper.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. namespace PF
  2. {
  3. /** What data to draw the graph debugging with */
  4. public enum GraphDebugMode {
  5. Areas,
  6. G,
  7. H,
  8. F,
  9. Penalty,
  10. Connections,
  11. Tags
  12. }
  13. /** How path results are logged by the system */
  14. public enum PathLog {
  15. /** Does not log anything. This is recommended for release since logging path results has a performance overhead. */
  16. None,
  17. /** Logs basic info about the paths */
  18. Normal,
  19. /** Includes additional info */
  20. Heavy,
  21. /** Same as heavy, but displays the info in-game using GUI */
  22. InGame,
  23. /** Same as normal, but logs only paths which returned an error */
  24. OnlyErrors
  25. }
  26. /** How to estimate the cost from to the destination.
  27. *
  28. * The heuristic is the estimated cost from the current node to the target.
  29. * The different heuristics have roughly the same performance except not using any heuristic at all (#None)
  30. * which is usually significantly slower.
  31. *
  32. * In the image below you can see a comparison of the different heuristic options for an 8-connected grid and
  33. * for a 4-connected grid.
  34. * Note that all paths within the green area will all have the same length. The only difference between the heuristics
  35. * is which of those paths of the same length that will be chosen.
  36. * Note that while the Diagonal Manhattan and Manhattan options seem to behave very differently on an 8-connected grid
  37. * they only do it in this case because of very small rounding errors. Usually they behave almost identically on 8-connected grids.
  38. *
  39. * \shadowimage{heuristic.png}
  40. *
  41. * Generally for a 4-connected grid graph the Manhattan option should be used as it is the true distance on a 4-connected grid.
  42. * For an 8-connected grid graph the Diagonal Manhattan option is the mathematically most correct option, however the Euclidean option
  43. * is often preferred, especially if you are simplifying the path afterwards using modifiers.
  44. *
  45. * For any graph that is not grid based the Euclidean option is the best one to use.
  46. *
  47. * \see <a href="https://en.wikipedia.org/wiki/A*_search_algorithm">Wikipedia: A* search_algorithm</a>
  48. */
  49. public enum Heuristic {
  50. /** Manhattan distance. \see https://en.wikipedia.org/wiki/Taxicab_geometry */
  51. Manhattan,
  52. /** Manhattan distance, but allowing diagonal movement as well.
  53. * \note This option is currently hard coded for the XZ plane. It will be equivalent to Manhattan distance if you try to use it in the XY plane (i.e for a 2D game).
  54. */
  55. DiagonalManhattan,
  56. /** Ordinary distance. \see https://en.wikipedia.org/wiki/Euclidean_distance */
  57. Euclidean,
  58. /** Use no heuristic at all.
  59. * This reduces the pathfinding algorithm to Dijkstra's algorithm.
  60. * This is usually significantly slower compared to using a heuristic, which is why the A* algorithm is usually preferred over Dijkstra's algorithm.
  61. * You may have to use this if you have a very non-standard graph. For example a world with a <a href="https://en.wikipedia.org/wiki/Wraparound_(video_games)">wraparound playfield</a> (think Civilization or Asteroids) and you have custom links
  62. * with a zero cost from one end of the map to the other end. Usually the A* algorithm wouldn't find the wraparound links because it wouldn't think to look in that direction.
  63. * \see https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
  64. */
  65. None
  66. }
  67. public static class PathFindHelper
  68. {
  69. #if !SERVER
  70. public static AstarPath GetConfig()
  71. {
  72. return AstarPath.active;
  73. }
  74. #else
  75. public static AStarConfig GetConfig()
  76. {
  77. return AStarConfig.Instance;
  78. }
  79. #endif
  80. /**
  81. * Returns the nearest node to a position using the specified NNConstraint.
  82. * Searches through all graphs for their nearest nodes to the specified position and picks the closest one.\n
  83. * Using the NNConstraint.None constraint.
  84. *
  85. * \snippet MiscSnippets.cs AstarPath.GetNearest1
  86. *
  87. * \see Pathfinding.NNConstraint
  88. */
  89. public static NNInfo GetNearest(Vector3 position)
  90. {
  91. return GetNearest(position, NNConstraint.None);
  92. }
  93. /**
  94. * Returns the nearest node to a position using the specified NNConstraint.
  95. * Searches through all graphs for their nearest nodes to the specified position and picks the closest one.
  96. * The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes.
  97. *
  98. * \snippet MiscSnippets.cs AstarPath.GetNearest2
  99. *
  100. * \snippet MiscSnippets.cs AstarPath.GetNearest3
  101. *
  102. * \see Pathfinding.NNConstraint
  103. */
  104. public static NNInfo GetNearest(Vector3 position, NNConstraint constraint)
  105. {
  106. return GetNearest(position, constraint, null);
  107. }
  108. /**
  109. * Returns the nearest node to a position using the specified NNConstraint.
  110. * Searches through all graphs for their nearest nodes to the specified position and picks the closest one.
  111. * The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes.
  112. * \see Pathfinding.NNConstraint
  113. */
  114. public static NNInfo GetNearest(Vector3 position, NNConstraint constraint, GraphNode hint)
  115. {
  116. // Cache property lookup
  117. var localGraphs = GetConfig().graphs;
  118. float minDist = float.PositiveInfinity;
  119. NNInfoInternal nearestNode = new NNInfoInternal();
  120. int nearestGraph = -1;
  121. if (localGraphs != null)
  122. {
  123. for (int i = 0; i < localGraphs.Length; i++)
  124. {
  125. NavGraph graph = localGraphs[i];
  126. // Check if this graph should be searched
  127. if (graph == null || !constraint.SuitableGraph(i, graph))
  128. {
  129. continue;
  130. }
  131. NNInfoInternal nnInfo;
  132. if (GetConfig().fullGetNearestSearch)
  133. {
  134. // Slower nearest node search
  135. // this will try to find a node which is suitable according to the constraint
  136. nnInfo = graph.GetNearestForce(position, constraint);
  137. }
  138. else
  139. {
  140. // Fast nearest node search
  141. // just find a node close to the position without using the constraint that much
  142. // (unless that comes essentially 'for free')
  143. nnInfo = graph.GetNearest(position, constraint);
  144. }
  145. GraphNode node = nnInfo.node;
  146. // No node found in this graph
  147. if (node == null)
  148. {
  149. continue;
  150. }
  151. // Distance to the closest point on the node from the requested position
  152. float dist = ((Vector3) nnInfo.clampedPosition - position).magnitude;
  153. if (GetConfig().prioritizeGraphs && dist < GetConfig().prioritizeGraphsLimit)
  154. {
  155. // The node is close enough, choose this graph and discard all others
  156. minDist = dist;
  157. nearestNode = nnInfo;
  158. nearestGraph = i;
  159. break;
  160. }
  161. else
  162. {
  163. // Choose the best node found so far
  164. if (dist < minDist)
  165. {
  166. minDist = dist;
  167. nearestNode = nnInfo;
  168. nearestGraph = i;
  169. }
  170. }
  171. }
  172. }
  173. // No matches found
  174. if (nearestGraph == -1)
  175. {
  176. return new NNInfo();
  177. }
  178. // Check if a constrained node has already been set
  179. if (nearestNode.constrainedNode != null)
  180. {
  181. nearestNode.node = nearestNode.constrainedNode;
  182. nearestNode.clampedPosition = nearestNode.constClampedPosition;
  183. }
  184. if (!GetConfig().fullGetNearestSearch && nearestNode.node != null && !constraint.Suitable(nearestNode.node))
  185. {
  186. // Otherwise, perform a check to force the graphs to check for a suitable node
  187. NNInfoInternal nnInfo = localGraphs[nearestGraph].GetNearestForce(position, constraint);
  188. if (nnInfo.node != null)
  189. {
  190. nearestNode = nnInfo;
  191. }
  192. }
  193. if (!constraint.Suitable(nearestNode.node) || (constraint.constrainDistance &&
  194. (nearestNode.clampedPosition - position).sqrMagnitude > GetConfig().maxNearestNodeDistanceSqr))
  195. {
  196. return new NNInfo();
  197. }
  198. // Convert to NNInfo which doesn't have all the internal fields
  199. return new NNInfo(nearestNode);
  200. }
  201. public static PathHandler debugPathData
  202. {
  203. get
  204. {
  205. return GetConfig().debugPathData;
  206. }
  207. set
  208. {
  209. GetConfig().debugPathData = value;
  210. }
  211. }
  212. public static ushort debugPathID
  213. {
  214. get
  215. {
  216. return GetConfig().debugPathID;
  217. }
  218. set
  219. {
  220. GetConfig().debugPathID = value;
  221. }
  222. }
  223. public static PathProcessor pathProcessor
  224. {
  225. get
  226. {
  227. return GetConfig().pathProcessor;
  228. }
  229. set
  230. {
  231. GetConfig().pathProcessor = value;
  232. }
  233. }
  234. public static bool IsUsingMultithreading {
  235. get {
  236. //return GetConfig().pathProcessor.IsUsingMultithreading;
  237. return false;
  238. }
  239. }
  240. public static NavGraph[] graphs
  241. {
  242. get {
  243. return GetConfig().graphs;
  244. }
  245. }
  246. public static Heuristic heuristic
  247. {
  248. get
  249. {
  250. return GetConfig().heuristic;
  251. }
  252. set
  253. {
  254. GetConfig().heuristic = value;
  255. }
  256. }
  257. public static float heuristicScale
  258. {
  259. get
  260. {
  261. return GetConfig().heuristicScale;
  262. }
  263. set
  264. {
  265. GetConfig().heuristicScale = value;
  266. }
  267. }
  268. public static EuclideanEmbedding euclideanEmbedding
  269. {
  270. get
  271. {
  272. return GetConfig().euclideanEmbedding;
  273. }
  274. set
  275. {
  276. GetConfig().euclideanEmbedding = value;
  277. }
  278. }
  279. public static PathLog logPathResults
  280. {
  281. get
  282. {
  283. return GetConfig().logPathResults;
  284. }
  285. set
  286. {
  287. GetConfig().logPathResults = value;
  288. }
  289. }
  290. /** Returns a new global node index.
  291. * \warning This method should not be called directly. It is used by the GraphNode constructor.
  292. */
  293. public static int GetNewNodeIndex () {
  294. return pathProcessor.GetNewNodeIndex();
  295. }
  296. /** Initializes temporary path data for a node.
  297. * \warning This method should not be called directly. It is used by the GraphNode constructor.
  298. */
  299. public static void InitializeNode (GraphNode node) {
  300. pathProcessor.InitializeNode(node);
  301. }
  302. /** @name Callbacks */
  303. /* Callbacks to pathfinding events.
  304. * These allow you to hook in to the pathfinding process.\n
  305. * Callbacks can be used like this:
  306. * \snippet MiscSnippets.cs AstarPath.Callbacks
  307. */
  308. /** @{ */
  309. /** Called on Awake before anything else is done.
  310. * This is called at the start of the Awake call, right after #active has been set, but this is the only thing that has been done.\n
  311. * Use this when you want to set up default settings for an AstarPath component created during runtime since some settings can only be changed in Awake
  312. * (such as multithreading related stuff)
  313. * \snippet MiscSnippets.cs AstarPath.OnAwakeSettings
  314. */
  315. public static System.Action OnAwakeSettings;
  316. /** Called for each path before searching. Be careful when using multithreading since this will be called from a different thread. */
  317. public static OnPathDelegate OnPathPreSearch;
  318. /** Called for each path after searching. Be careful when using multithreading since this will be called from a different thread. */
  319. public static OnPathDelegate OnPathPostSearch;
  320. /**
  321. * Called when \a pathID overflows 65536 and resets back to zero.
  322. * \note This callback will be cleared every time it is called, so if you want to register to it repeatedly, register to it directly on receiving the callback as well.
  323. */
  324. public static System.Action On65KOverflow;
  325. /** The next unused Path ID.
  326. * Incremented for every call to GetNextPathID
  327. */
  328. private static ushort nextFreePathID = 1;
  329. /** Returns the next free path ID */
  330. public static ushort GetNextPathID () {
  331. if (nextFreePathID == 0) {
  332. nextFreePathID++;
  333. if (On65KOverflow != null) {
  334. System.Action tmp = On65KOverflow;
  335. On65KOverflow = null;
  336. tmp();
  337. }
  338. }
  339. return nextFreePathID++;
  340. }
  341. public static void Close()
  342. {
  343. OnAwakeSettings = null;
  344. OnPathPreSearch = null;
  345. OnPathPostSearch = null;
  346. On65KOverflow = null;
  347. }
  348. }
  349. }