PathFindHelper.cs 14 KB

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