Path.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. //#define ASTAR_POOL_DEBUG //@SHOWINEDITOR Enables debugging of path pooling. Will log warnings and info messages about paths not beeing pooled correctly.
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace PF {
  5. #region Enums
  6. public enum GraphUpdateThreading {
  7. /** Call UpdateArea in the unity thread.
  8. * This is the default value.
  9. * Not compatible with SeparateThread.
  10. */
  11. UnityThread = 0,
  12. /** Call UpdateArea in a separate thread. Not compatible with UnityThread. */
  13. SeparateThread = 1 << 0,
  14. /** Calls UpdateAreaInit in the Unity thread before everything else */
  15. UnityInit = 1 << 1,
  16. /** Calls UpdateAreaPost in the Unity thread after everything else.
  17. * This is used together with SeparateThread to apply the result of the multithreaded
  18. * calculations to the graph without modifying it at the same time as some other script
  19. * might be using it (e.g calling GetNearest).
  20. */
  21. UnityPost = 1 << 2,
  22. /** Combination of SeparateThread and UnityInit */
  23. SeparateAndUnityInit = SeparateThread | UnityInit
  24. }
  25. /** Number of threads to use */
  26. public enum ThreadCount {
  27. AutomaticLowLoad = -1,
  28. AutomaticHighLoad = -2,
  29. None = 0,
  30. One = 1,
  31. Two,
  32. Three,
  33. Four,
  34. Five,
  35. Six,
  36. Seven,
  37. Eight
  38. }
  39. /** Internal state of a path in the pipeline */
  40. public enum PathState {
  41. Created = 0,
  42. PathQueue = 1,
  43. Processing = 2,
  44. ReturnQueue = 3,
  45. Returned = 4
  46. }
  47. /** State of a path request */
  48. public enum PathCompleteState {
  49. /** The path has not been calculated yet.
  50. * \see #Pathfinding.Path.IsDone()
  51. */
  52. NotCalculated = 0,
  53. /** The path calculation is done, but it failed.
  54. * \see #Pathfinding.Path.error
  55. */
  56. Error = 1,
  57. /** The path has been successfully calculated */
  58. Complete = 2,
  59. /** The path has been calculated, but only a partial path could be found.
  60. * \see #Pathfinding.ABPath.calculatePartial
  61. */
  62. Partial = 3,
  63. }
  64. /** What to do when the character is close to the destination */
  65. public enum CloseToDestinationMode {
  66. /** The character will stop as quickly as possible when within \a endReachedDistance (field that exist on most movement scripts) units from the destination */
  67. Stop,
  68. /** The character will continue to the exact position of the destination */
  69. ContinueToExactDestination,
  70. }
  71. /** Indicates the side of a line that a point lies on */
  72. public enum Side : byte {
  73. /** The point lies exactly on the line */
  74. Colinear = 0,
  75. /** The point lies on the left side of the line */
  76. Left = 1,
  77. /** The point lies on the right side of the line */
  78. Right = 2
  79. }
  80. public enum InspectorGridMode {
  81. Grid,
  82. IsometricGrid,
  83. Hexagonal,
  84. Advanced
  85. }
  86. #endregion
  87. #region Delegates
  88. /* Delegate with on Path object as parameter.
  89. * This is used for callbacks when a path has finished calculation.\n
  90. * Example function:
  91. * \snippet MiscSnippets.cs OnPathDelegate
  92. */
  93. public delegate void OnPathDelegate (Path p);
  94. #endregion
  95. /** Provides additional traversal information to a path request.
  96. * \see \ref turnbased
  97. */
  98. public interface ITraversalProvider {
  99. bool CanTraverse (Path path, GraphNode node);
  100. uint GetTraversalCost (Path path, GraphNode node);
  101. }
  102. /** Convenience class to access the default implementation of the ITraversalProvider */
  103. public static class DefaultITraversalProvider {
  104. public static bool CanTraverse (Path path, GraphNode node) {
  105. return node.Walkable && (path.enabledTags >> (int)node.Tag & 0x1) != 0;
  106. }
  107. public static uint GetTraversalCost (Path path, GraphNode node) {
  108. return path.GetTagPenalty((int)node.Tag) + node.Penalty;
  109. }
  110. }
  111. /** Base class for all path types */
  112. public abstract class Path : IPathInternals {
  113. #if ASTAR_POOL_DEBUG
  114. private string pathTraceInfo = "";
  115. private List<string> claimInfo = new List<string>();
  116. ~Path() {
  117. Debug.Log("Destroying " + GetType().Name + " instance");
  118. if (claimed.Count > 0) {
  119. Debug.LogWarning("Pool Is Leaking. See list of claims:\n" +
  120. "Each message below will list what objects are currently claiming the path." +
  121. " These objects have removed their reference to the path object but has not called .Release on it (which is bad).\n" + pathTraceInfo+"\n");
  122. for (int i = 0; i < claimed.Count; i++) {
  123. Debug.LogWarning("- Claim "+ (i+1) + " is by a " + claimed[i].GetType().Name + "\n"+claimInfo[i]);
  124. }
  125. } else {
  126. Debug.Log("Some scripts are not using pooling.\n" + pathTraceInfo + "\n");
  127. }
  128. }
  129. #endif
  130. /** Data for the thread calculating this path */
  131. protected PathHandler pathHandler;
  132. /** Callback to call when the path is complete.
  133. * This is usually sent to the Seeker component which post processes the path and then calls a callback to the script which requested the path
  134. */
  135. public OnPathDelegate callback;
  136. /** Immediate callback to call when the path is complete.
  137. * \warning This may be called from a separate thread. Usually you do not want to use this one.
  138. *
  139. * \see callback
  140. */
  141. public OnPathDelegate immediateCallback;
  142. /** Returns the state of the path in the pathfinding pipeline */
  143. internal PathState PipelineState { get; private set; }
  144. System.Object stateLock = new object ();
  145. /** Provides additional traversal information to a path request.
  146. * \see \ref turnbased
  147. */
  148. public ITraversalProvider traversalProvider;
  149. /** Backing field for #CompleteState */
  150. protected PathCompleteState completeState;
  151. /** Current state of the path */
  152. public PathCompleteState CompleteState {
  153. get { return completeState; }
  154. protected set {
  155. // Locking is used to avoid multithreading race conditions
  156. // in which the error state is set on the main thread to cancel the path and then a pathfinding thread marks the path as
  157. // completed which would replace the error state (if a lock and check would not have been used).
  158. lock (stateLock) {
  159. // Once the path is put in the error state, it cannot be set to any other state
  160. if (completeState != PathCompleteState.Error) completeState = value;
  161. }
  162. }
  163. }
  164. /** If the path failed, this is true.
  165. * \see #errorLog
  166. * \see This is equivalent to checking path.CompleteState == PathCompleteState.Error
  167. */
  168. public bool error { get { return CompleteState == PathCompleteState.Error; } }
  169. /** Additional info on why a path failed.
  170. * \see #AstarPath.logPathResults
  171. */
  172. public string errorLog { get; private set; }
  173. /** Holds the path as a Node array. All nodes the path traverses.
  174. * This may not be the same nodes as the post processed path traverses.
  175. */
  176. public List<GraphNode> path;
  177. /** Holds the (possibly post processed) path as a Vector3 list */
  178. public List<PF.Vector3> vectorPath;
  179. /** The node currently being processed */
  180. protected PathNode currentR;
  181. /** How long it took to calculate this path in milliseconds */
  182. internal float duration;
  183. /** Number of nodes this path has searched */
  184. internal int searchedNodes;
  185. /** True if the path is currently pooled.
  186. * Do not set this value. Only read. It is used internally.
  187. *
  188. * \see PathPool
  189. * \version Was named 'recycled' in 3.7.5 and earlier.
  190. */
  191. bool IPathInternals.Pooled { get; set; }
  192. /** True if the path is currently recycled (i.e in the path pool).
  193. * Do not set this value. Only read. It is used internally.
  194. *
  195. * \deprecated Has been renamed to 'pooled' to use more widely underestood terminology
  196. */
  197. [System.Obsolete("Has been renamed to 'Pooled' to use more widely underestood terminology", true)]
  198. internal bool recycled { get { return false; } }
  199. /** True if the Reset function has been called.
  200. * Used to alert users when they are doing something wrong.
  201. */
  202. protected bool hasBeenReset;
  203. /** Constraint for how to search for nodes */
  204. public NNConstraint nnConstraint = PathNNConstraint.Default;
  205. /** Internal linked list implementation.
  206. * \warning This is used internally by the system. You should never change this.
  207. */
  208. internal Path next;
  209. /** Determines which heuristic to use */
  210. public Heuristic heuristic;
  211. /** Scale of the heuristic values.
  212. * \see AstarPath.heuristicScale
  213. */
  214. public float heuristicScale = 1F;
  215. /** ID of this path. Used to distinguish between different paths */
  216. internal ushort pathID { get; private set; }
  217. /** Target to use for H score calculation. Used alongside #hTarget. */
  218. protected GraphNode hTargetNode;
  219. /** Target to use for H score calculations. \see Pathfinding.Node.H */
  220. protected Int3 hTarget;
  221. /** Which graph tags are traversable.
  222. * This is a bitmask so -1 = all bits set = all tags traversable.
  223. * For example, to set bit 5 to true, you would do
  224. * \code myPath.enabledTags |= 1 << 5; \endcode
  225. * To set it to false, you would do
  226. * \code myPath.enabledTags &= ~(1 << 5); \endcode
  227. *
  228. * The Seeker has a popup field where you can set which tags to use.
  229. * \note If you are using a Seeker. The Seeker will set this value to what is set in the inspector field on StartPath.
  230. * So you need to change the Seeker value via script, not set this value if you want to change it via script.
  231. *
  232. * \see CanTraverse
  233. */
  234. public int enabledTags = -1;
  235. /** List of zeroes to use as default tag penalties */
  236. static readonly int[] ZeroTagPenalties = new int[32];
  237. /** The tag penalties that are actually used.
  238. * If manualTagPenalties is null, this will be ZeroTagPenalties
  239. * \see tagPenalties
  240. */
  241. protected int[] internalTagPenalties;
  242. /** Tag penalties set by other scripts
  243. * \see tagPenalties
  244. */
  245. protected int[] manualTagPenalties;
  246. /** Penalties for each tag.
  247. * Tag 0 which is the default tag, will have added a penalty of tagPenalties[0].
  248. * These should only be positive values since the A* algorithm cannot handle negative penalties.
  249. * \note This array will never be null. If you try to set it to null or with a length which is not 32. It will be set to "new int[0]".
  250. *
  251. * \note If you are using a Seeker. The Seeker will set this value to what is set in the inspector field on StartPath.
  252. * So you need to change the Seeker value via script, not set this value if you want to change it via script.
  253. *
  254. * \see Seeker.tagPenalties
  255. */
  256. public int[] tagPenalties {
  257. get {
  258. return manualTagPenalties;
  259. }
  260. set {
  261. if (value == null || value.Length != 32) {
  262. manualTagPenalties = null;
  263. internalTagPenalties = ZeroTagPenalties;
  264. } else {
  265. manualTagPenalties = value;
  266. internalTagPenalties = value;
  267. }
  268. }
  269. }
  270. /** True for paths that want to search all nodes and not jump over nodes as optimizations.
  271. * This disables Jump Point Search when that is enabled to prevent e.g ConstantPath and FloodPath
  272. * to become completely useless.
  273. */
  274. internal virtual bool FloodingPath {
  275. get {
  276. return false;
  277. }
  278. }
  279. /** Total Length of the path.
  280. * Calculates the total length of the #vectorPath.
  281. * Cache this rather than call this function every time since it will calculate the length every time, not just return a cached value.
  282. * \returns Total length of #vectorPath, if #vectorPath is null positive infinity is returned.
  283. */
  284. public float GetTotalLength () {
  285. if (vectorPath == null) return float.PositiveInfinity;
  286. float tot = 0;
  287. for (int i = 0; i < vectorPath.Count-1; i++) tot += Vector3.Distance(vectorPath[i], vectorPath[i+1]);
  288. return tot;
  289. }
  290. /** Waits until this path has been calculated and returned.
  291. * Allows for very easy scripting.
  292. * \code
  293. * IEnumerator Start () {
  294. * var path = seeker.StartPath(transform.position, transform.position + transform.forward*10, null);
  295. * yield return StartCoroutine(path.WaitForPath());
  296. * // The path is calculated now
  297. * }
  298. * \endcode
  299. *
  300. * \note Do not confuse this with AstarPath.WaitForPath. This one will wait using yield until it has been calculated
  301. * while AstarPath.WaitForPath will halt all operations until the path has been calculated.
  302. *
  303. * \throws System.InvalidOperationException if the path is not started. Send the path to Seeker.StartPath or AstarPath.StartPath before calling this function.
  304. *
  305. * \see #BlockUntilCalculated
  306. * \see https://docs.unity3d.com/Manual/Coroutines.html
  307. */
  308. public IEnumerator WaitForPath () {
  309. if (PipelineState == PathState.Created) throw new System.InvalidOperationException("This path has not been started yet");
  310. while (PipelineState != PathState.Returned) yield return null;
  311. }
  312. /** Estimated cost from the specified node to the target.
  313. * \see https://en.wikipedia.org/wiki/A*_search_algorithm
  314. */
  315. internal uint CalculateHScore (GraphNode node) {
  316. uint h;
  317. switch (heuristic) {
  318. case Heuristic.Euclidean:
  319. h = (uint)(((GetHTarget() - node.position).costMagnitude)*heuristicScale);
  320. // Inlining this check and the return
  321. // for each case saves an extra jump.
  322. // This code is pretty hot
  323. if (hTargetNode != null) {
  324. h = System.Math.Max(h, PathFindHelper.euclideanEmbedding.GetHeuristic(node.NodeIndex, hTargetNode.NodeIndex));
  325. }
  326. return h;
  327. case Heuristic.Manhattan:
  328. Int3 p2 = node.position;
  329. h = (uint)((System.Math.Abs(hTarget.x-p2.x) + System.Math.Abs(hTarget.y-p2.y) + System.Math.Abs(hTarget.z-p2.z))*heuristicScale);
  330. if (hTargetNode != null) {
  331. h = System.Math.Max(h, PathFindHelper.GetConfig().euclideanEmbedding.GetHeuristic(node.NodeIndex, hTargetNode.NodeIndex));
  332. }
  333. return h;
  334. case Heuristic.DiagonalManhattan:
  335. Int3 p = GetHTarget() - node.position;
  336. p.x = System.Math.Abs(p.x);
  337. p.y = System.Math.Abs(p.y);
  338. p.z = System.Math.Abs(p.z);
  339. int diag = System.Math.Min(p.x, p.z);
  340. int diag2 = System.Math.Max(p.x, p.z);
  341. h = (uint)((((14*diag)/10) + (diag2-diag) + p.y) * heuristicScale);
  342. if (hTargetNode != null) {
  343. h = System.Math.Max(h, PathFindHelper.euclideanEmbedding.GetHeuristic(node.NodeIndex, hTargetNode.NodeIndex));
  344. }
  345. return h;
  346. }
  347. return 0U;
  348. }
  349. /** Returns penalty for the given tag.
  350. * \param tag A value between 0 (inclusive) and 32 (exclusive).
  351. */
  352. internal uint GetTagPenalty (int tag) {
  353. return (uint)internalTagPenalties[tag];
  354. }
  355. internal Int3 GetHTarget () {
  356. return hTarget;
  357. }
  358. /** Returns if the node can be traversed.
  359. * This per default equals to if the node is walkable and if the node's tag is included in #enabledTags */
  360. internal bool CanTraverse (GraphNode node) {
  361. // Use traversal provider if set, otherwise fall back on default behaviour
  362. // This method is hot, but this branch is extremely well predicted so it
  363. // doesn't affect performance much (profiling indicates it is just above
  364. // the noise level, somewhere around 0%-0.3%)
  365. if (traversalProvider != null)
  366. return traversalProvider.CanTraverse(this, node);
  367. unchecked { return node.Walkable && (enabledTags >> (int)node.Tag & 0x1) != 0; }
  368. }
  369. internal uint GetTraversalCost (GraphNode node) {
  370. #if ASTAR_NO_TRAVERSAL_COST
  371. return 0;
  372. #else
  373. // Use traversal provider if set, otherwise fall back on default behaviour
  374. if (traversalProvider != null)
  375. return traversalProvider.GetTraversalCost(this, node);
  376. unchecked { return GetTagPenalty((int)node.Tag) + node.Penalty; }
  377. #endif
  378. }
  379. /** May be called by graph nodes to get a special cost for some connections.
  380. * Nodes may call it when PathNode.flag2 is set to true, for example mesh nodes, which have
  381. * a very large area can be marked on the start and end nodes, this method will be called
  382. * to get the actual cost for moving from the start position to its neighbours instead
  383. * of as would otherwise be the case, from the start node's position to its neighbours.
  384. * The position of a node and the actual start point on the node can vary quite a lot.
  385. *
  386. * The default behaviour of this method is to return the previous cost of the connection,
  387. * essentiall making no change at all.
  388. *
  389. * This method should return the same regardless of the order of a and b.
  390. * That is f(a,b) == f(b,a) should hold.
  391. *
  392. * \param a Moving from this node
  393. * \param b Moving to this node
  394. * \param currentCost The cost of moving between the nodes. Return this value if there is no meaningful special cost to return.
  395. */
  396. internal virtual uint GetConnectionSpecialCost (GraphNode a, GraphNode b, uint currentCost) {
  397. return currentCost;
  398. }
  399. /** Returns if this path is done calculating.
  400. * \returns If #CompleteState is not \link Pathfinding.PathCompleteState.NotCalculated NotCalculated\endlink.
  401. *
  402. * \note The callback for the path might not have been called yet.
  403. *
  404. * \since Added in 3.0.8
  405. *
  406. * \see #Seeker.IsDone which also takes into account if the %path %callback has been called and had modifiers applied.
  407. */
  408. public bool IsDone () {
  409. return CompleteState != PathCompleteState.NotCalculated;
  410. }
  411. /** Threadsafe increment of the state */
  412. void IPathInternals.AdvanceState (PathState s) {
  413. lock (stateLock) {
  414. PipelineState = (PathState)System.Math.Max((int)PipelineState, (int)s);
  415. }
  416. }
  417. /** Returns the state of the path in the pathfinding pipeline.
  418. * \deprecated Use the #Pathfinding.Path.PipelineState property instead
  419. */
  420. [System.Obsolete("Use the 'PipelineState' property instead")]
  421. public PathState GetState () {
  422. return PipelineState;
  423. }
  424. /** Causes the path to fail and sets #errorLog to \a msg */
  425. internal void FailWithError (string msg) {
  426. Error();
  427. if (errorLog != "") errorLog += "\n" + msg;
  428. else errorLog = msg;
  429. }
  430. /** Logs an error.
  431. * \deprecated Use #FailWithError instead
  432. */
  433. [System.Obsolete("Use FailWithError instead")]
  434. internal void LogError (string msg) {
  435. Log(msg);
  436. }
  437. /** Appends a message to the #errorLog.
  438. * Nothing is logged to the console.
  439. *
  440. * \note If AstarPath.logPathResults is PathLog.None and this is a standalone player, nothing will be logged as an optimization.
  441. *
  442. * \deprecated Use #FailWithError instead
  443. */
  444. [System.Obsolete("Use FailWithError instead")]
  445. internal void Log (string msg) {
  446. errorLog += msg;
  447. }
  448. /** Aborts the path because of an error.
  449. * Sets #error to true.
  450. * This function is called when an error has occurred (e.g a valid path could not be found).
  451. * \see #FailWithError
  452. */
  453. public void Error () {
  454. CompleteState = PathCompleteState.Error;
  455. }
  456. /** Performs some error checking.
  457. * Makes sure the user isn't using old code paths and that no major errors have been made.
  458. *
  459. * Causes the path to fail if any errors are found.
  460. */
  461. private void ErrorCheck () {
  462. if (!hasBeenReset) FailWithError("Please use the static Construct function for creating paths, do not use the normal constructors.");
  463. if (((IPathInternals)this).Pooled) FailWithError("The path is currently in a path pool. Are you sending the path for calculation twice?");
  464. if (pathHandler == null) FailWithError("Field pathHandler is not set. Please report this bug.");
  465. if (PipelineState > PathState.Processing) FailWithError("This path has already been processed. Do not request a path with the same path object twice.");
  466. }
  467. /** Called when the path enters the pool.
  468. * This method should release e.g pooled lists and other pooled resources
  469. * The base version of this method releases vectorPath and path lists.
  470. * Reset() will be called after this function, not before.
  471. * \warning Do not call this function manually.
  472. */
  473. protected virtual void OnEnterPool () {
  474. if (vectorPath != null) ListPool<Vector3>.Release(ref vectorPath);
  475. if (path != null) ListPool<GraphNode>.Release(ref path);
  476. // Clear the callback to remove a potential memory leak
  477. // while the path is in the pool (which it could be for a long time).
  478. callback = null;
  479. immediateCallback = null;
  480. traversalProvider = null;
  481. }
  482. /** Reset all values to their default values.
  483. *
  484. * \note All inheriting path types (e.g ConstantPath, RandomPath, etc.) which declare their own variables need to
  485. * override this function, resetting ALL their variables to enable pooling of paths.
  486. * If this is not done, trying to use that path type for pooling could result in weird behaviour.
  487. * The best way is to reset to default values the variables declared in the extended path type and then
  488. * call the base function in inheriting types with base.Reset().
  489. */
  490. protected virtual void Reset () {
  491. #if ASTAR_POOL_DEBUG
  492. pathTraceInfo = "This path was got from the pool or created from here (stacktrace):\n";
  493. pathTraceInfo += System.Environment.StackTrace;
  494. #endif
  495. hasBeenReset = true;
  496. PipelineState = (int)PathState.Created;
  497. releasedNotSilent = false;
  498. pathHandler = null;
  499. callback = null;
  500. immediateCallback = null;
  501. errorLog = "";
  502. completeState = PathCompleteState.NotCalculated;
  503. path = ListPool<GraphNode>.Claim();
  504. vectorPath = ListPool<Vector3>.Claim();
  505. currentR = null;
  506. duration = 0;
  507. searchedNodes = 0;
  508. nnConstraint = PathNNConstraint.Default;
  509. next = null;
  510. heuristic = PathFindHelper.heuristic;
  511. heuristicScale = PathFindHelper.heuristicScale;
  512. enabledTags = -1;
  513. tagPenalties = null;
  514. pathID = PathFindHelper.GetNextPathID();
  515. hTarget = Int3.zero;
  516. hTargetNode = null;
  517. traversalProvider = null;
  518. }
  519. /** List of claims on this path with reference objects */
  520. private List<System.Object> claimed = new List<System.Object>();
  521. /** True if the path has been released with a non-silent call yet.
  522. *
  523. * \see Release
  524. * \see Claim
  525. */
  526. private bool releasedNotSilent;
  527. /** Claim this path (pooling).
  528. * A claim on a path will ensure that it is not pooled.
  529. * If you are using a path, you will want to claim it when you first get it and then release it when you will not
  530. * use it anymore. When there are no claims on the path, it will be reset and put in a pool.
  531. *
  532. * This is essentially just reference counting.
  533. *
  534. * The object passed to this method is merely used as a way to more easily detect when pooling is not done correctly.
  535. * It can be any object, when used from a movement script you can just pass "this". This class will throw an exception
  536. * if you try to call Claim on the same path twice with the same object (which is usually not what you want) or
  537. * if you try to call Release with an object that has not been used in a Claim call for that path.
  538. * The object passed to the Claim method needs to be the same as the one you pass to this method.
  539. *
  540. * \see Release
  541. * \see Pool
  542. * \see \ref pooling
  543. * \see https://en.wikipedia.org/wiki/Reference_counting
  544. */
  545. public void Claim (System.Object o) {
  546. if (System.Object.ReferenceEquals(o, null)) throw new System.ArgumentNullException("o");
  547. for (int i = 0; i < claimed.Count; i++) {
  548. // Need to use ReferenceEquals because it might be called from another thread
  549. if (System.Object.ReferenceEquals(claimed[i], o))
  550. throw new System.ArgumentException("You have already claimed the path with that object ("+o+"). Are you claiming the path with the same object twice?");
  551. }
  552. claimed.Add(o);
  553. #if ASTAR_POOL_DEBUG
  554. claimInfo.Add(o.ToString() + "\n\nClaimed from:\n" + System.Environment.StackTrace);
  555. #endif
  556. }
  557. /** Releases the path silently (pooling).
  558. * \deprecated Use Release(o, true) instead
  559. */
  560. [System.Obsolete("Use Release(o, true) instead")]
  561. internal void ReleaseSilent (System.Object o) {
  562. Release(o, true);
  563. }
  564. /** Releases a path claim (pooling).
  565. * Removes the claim of the path by the specified object.
  566. * When the claim count reaches zero, the path will be pooled, all variables will be cleared and the path will be put in a pool to be used again.
  567. * This is great for performance since fewer allocations are made.
  568. *
  569. * If the silent parameter is true, this method will remove the claim by the specified object
  570. * but the path will not be pooled if the claim count reches zero unless a Release call (not silent) has been made earlier.
  571. * This is used by the internal pathfinding components such as Seeker and AstarPath so that they will not cause paths to be pooled.
  572. * This enables users to skip the claim/release calls if they want without the path being pooled by the Seeker or AstarPath and
  573. * thus causing strange bugs.
  574. *
  575. * \see Claim
  576. * \see PathPool
  577. */
  578. public void Release (System.Object o, bool silent = false) {
  579. if (o == null) throw new System.ArgumentNullException("o");
  580. for (int i = 0; i < claimed.Count; i++) {
  581. // Need to use ReferenceEquals because it might be called from another thread
  582. if (System.Object.ReferenceEquals(claimed[i], o)) {
  583. claimed.RemoveAt(i);
  584. #if ASTAR_POOL_DEBUG
  585. claimInfo.RemoveAt(i);
  586. #endif
  587. if (!silent) {
  588. releasedNotSilent = true;
  589. }
  590. if (claimed.Count == 0 && releasedNotSilent) {
  591. PathPool.Pool(this);
  592. }
  593. return;
  594. }
  595. }
  596. if (claimed.Count == 0) {
  597. throw new System.ArgumentException("You are releasing a path which is not claimed at all (most likely it has been pooled already). " +
  598. "Are you releasing the path with the same object ("+o+") twice?" +
  599. "\nCheck out the documentation on path pooling for help.");
  600. }
  601. throw new System.ArgumentException("You are releasing a path which has not been claimed with this object ("+o+"). " +
  602. "Are you releasing the path with the same object twice?\n" +
  603. "Check out the documentation on path pooling for help.");
  604. }
  605. /** Traces the calculated path from the end node to the start.
  606. * This will build an array (#path) of the nodes this path will pass through and also set the #vectorPath array to the #path arrays positions.
  607. * Assumes the #vectorPath and #path are empty and not null (which will be the case for a correctly initialized path).
  608. */
  609. protected virtual void Trace (PathNode from) {
  610. // Current node we are processing
  611. PathNode c = from;
  612. int count = 0;
  613. while (c != null) {
  614. c = c.parent;
  615. count++;
  616. if (count > 2048) {
  617. #if !SERVER
  618. UnityEngine.Debug.LogWarning("Infinite loop? >2048 node path. Remove this message if you really have that long paths (Path.cs, Trace method)");
  619. #endif
  620. break;
  621. }
  622. }
  623. if (path.Capacity < count) path.Capacity = count;
  624. if (vectorPath.Capacity < count) vectorPath.Capacity = count;
  625. c = from;
  626. for (int i = 0; i < count; i++) {
  627. path.Add(c.node);
  628. c = c.parent;
  629. }
  630. // Reverse
  631. int half = count/2;
  632. for (int i = 0; i < half; i++) {
  633. var tmp = path[i];
  634. path[i] = path[count-i-1];
  635. path[count - i - 1] = tmp;
  636. }
  637. for (int i = 0; i < count; i++) {
  638. vectorPath.Add((Vector3)path[i].position);
  639. }
  640. }
  641. /** Writes text shared for all overrides of DebugString to the string builder.
  642. * \see DebugString
  643. */
  644. protected void DebugStringPrefix (PathLog logMode, System.Text.StringBuilder text) {
  645. text.Append(error ? "Path Failed : " : "Path Completed : ");
  646. text.Append("Computation Time ");
  647. text.Append(duration.ToString(logMode == PathLog.Heavy ? "0.000 ms " : "0.00 ms "));
  648. text.Append("Searched Nodes ").Append(searchedNodes);
  649. if (!error) {
  650. text.Append(" Path Length ");
  651. text.Append(path == null ? "Null" : path.Count.ToString());
  652. }
  653. }
  654. /** Writes text shared for all overrides of DebugString to the string builder.
  655. * \see DebugString
  656. */
  657. protected void DebugStringSuffix (PathLog logMode, System.Text.StringBuilder text) {
  658. if (error) {
  659. text.Append("\nError: ").Append(errorLog);
  660. }
  661. // Can only print this from the Unity thread
  662. // since otherwise an exception might be thrown
  663. if (logMode == PathLog.Heavy && !PathFindHelper.IsUsingMultithreading) {
  664. text.Append("\nCallback references ");
  665. if (callback != null) text.Append(callback.Target.GetType().FullName).AppendLine();
  666. else text.AppendLine("NULL");
  667. }
  668. text.Append("\nPath Number ").Append(pathID).Append(" (unique id)");
  669. }
  670. /** Returns a string with information about it.
  671. * More information is emitted when logMode == Heavy.
  672. * An empty string is returned if logMode == None
  673. * or logMode == OnlyErrors and this path did not fail.
  674. */
  675. internal virtual string DebugString (PathLog logMode) {
  676. if (logMode == PathLog.None || (!error && logMode == PathLog.OnlyErrors)) {
  677. return "";
  678. }
  679. // Get a cached string builder for this thread
  680. System.Text.StringBuilder text = pathHandler.DebugStringBuilder;
  681. text.Length = 0;
  682. DebugStringPrefix(logMode, text);
  683. DebugStringSuffix(logMode, text);
  684. return text.ToString();
  685. }
  686. /** Calls callback to return the calculated path. \see #callback */
  687. protected virtual void ReturnPath () {
  688. if (callback != null) {
  689. callback(this);
  690. }
  691. }
  692. /** Prepares low level path variables for calculation.
  693. * Called before a path search will take place.
  694. * Always called before the Prepare, Initialize and CalculateStep functions
  695. */
  696. protected void PrepareBase (PathHandler pathHandler) {
  697. //Path IDs have overflowed 65K, cleanup is needed
  698. //Since pathIDs are handed out sequentially, we can do this
  699. if (pathHandler.PathID > pathID) {
  700. pathHandler.ClearPathIDs();
  701. }
  702. //Make sure the path has a reference to the pathHandler
  703. this.pathHandler = pathHandler;
  704. //Assign relevant path data to the pathHandler
  705. pathHandler.InitializeForPath(this);
  706. // Make sure that internalTagPenalties is an array which has the length 32
  707. if (internalTagPenalties == null || internalTagPenalties.Length != 32)
  708. internalTagPenalties = ZeroTagPenalties;
  709. try {
  710. ErrorCheck();
  711. } catch (System.Exception e) {
  712. FailWithError(e.Message);
  713. }
  714. }
  715. /** Called before the path is started.
  716. * Called right before Initialize
  717. */
  718. protected abstract void Prepare ();
  719. /** Always called after the path has been calculated.
  720. * Guaranteed to be called before other paths have been calculated on
  721. * the same thread.
  722. * Use for cleaning up things like node tagging and similar.
  723. */
  724. protected virtual void Cleanup () {}
  725. /** Initializes the path.
  726. * Sets up the open list and adds the first node to it
  727. */
  728. protected abstract void Initialize ();
  729. /** Calculates the until it is complete or the time has progressed past \a targetTick */
  730. protected abstract void CalculateStep (long targetTick);
  731. PathHandler IPathInternals.PathHandler { get { return pathHandler; } }
  732. void IPathInternals.OnEnterPool () { OnEnterPool(); }
  733. void IPathInternals.Reset () { Reset(); }
  734. void IPathInternals.ReturnPath () { ReturnPath(); }
  735. void IPathInternals.PrepareBase (PathHandler handler) { PrepareBase(handler); }
  736. void IPathInternals.Prepare () { Prepare(); }
  737. void IPathInternals.Cleanup () { Cleanup(); }
  738. void IPathInternals.Initialize () { Initialize(); }
  739. void IPathInternals.CalculateStep (long targetTick) { CalculateStep(targetTick); }
  740. }
  741. /** Used for hiding internal methods of the Path class */
  742. internal interface IPathInternals {
  743. PathHandler PathHandler { get; }
  744. bool Pooled { get; set; }
  745. void AdvanceState (PathState s);
  746. void OnEnterPool ();
  747. void Reset ();
  748. void ReturnPath ();
  749. void PrepareBase (PathHandler handler);
  750. void Prepare ();
  751. void Initialize ();
  752. void Cleanup ();
  753. void CalculateStep (long targetTick);
  754. }
  755. }