Path.cs 31 KB

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