Path.cs 32 KB

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