AstarPath.cs 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Pathfinding;
  5. #if UNITY_5_5_OR_NEWER
  6. using UnityEngine.Profiling;
  7. #endif
  8. #if NETFX_CORE
  9. using Thread = Pathfinding.WindowsStore.Thread;
  10. #else
  11. using Thread = System.Threading.Thread;
  12. #endif
  13. [ExecuteInEditMode]
  14. [AddComponentMenu("Pathfinding/Pathfinder")]
  15. /// <summary>
  16. /// Core component for the A* Pathfinding System.
  17. /// This class handles all of the pathfinding system, calculates all paths and stores the info.
  18. /// This class is a singleton class, meaning there should only exist at most one active instance of it in the scene.
  19. /// It might be a bit hard to use directly, usually interfacing with the pathfinding system is done through the <see cref="Pathfinding.Seeker"/> class.
  20. /// </summary>
  21. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_astar_path.php")]
  22. public class AstarPath : VersionedMonoBehaviour {
  23. /// <summary>The version number for the A* Pathfinding Project</summary>
  24. public static readonly System.Version Version = new System.Version(4, 2, 18);
  25. /// <summary>Information about where the package was downloaded</summary>
  26. public enum AstarDistribution { WebsiteDownload, AssetStore, PackageManager };
  27. /// <summary>Used by the editor to guide the user to the correct place to download updates</summary>
  28. public static readonly AstarDistribution Distribution = AstarDistribution.AssetStore;
  29. /// <summary>
  30. /// Which branch of the A* Pathfinding Project is this release.
  31. /// Used when checking for updates so that
  32. /// users of the development versions can get notifications of development
  33. /// updates.
  34. /// </summary>
  35. public static readonly string Branch = "master";
  36. /// <summary>
  37. /// See Pathfinding.AstarData
  38. /// Deprecated:
  39. /// </summary>
  40. [System.Obsolete]
  41. public System.Type[] graphTypes {
  42. get {
  43. return data.graphTypes;
  44. }
  45. }
  46. /// <summary>Holds all graph data</summary>
  47. [UnityEngine.Serialization.FormerlySerializedAs("astarData")]
  48. public AstarData data;
  49. /// <summary>
  50. /// Holds all graph data.
  51. /// Deprecated: The 'astarData' field has been renamed to 'data'
  52. /// </summary>
  53. [System.Obsolete("The 'astarData' field has been renamed to 'data'")]
  54. public AstarData astarData { get { return data; } }
  55. /// <summary>
  56. /// Returns the active AstarPath object in the scene.
  57. /// Note: This is only set if the AstarPath object has been initialized (which happens in Awake).
  58. /// </summary>
  59. #if UNITY_4_6 || UNITY_4_3
  60. public static new AstarPath active;
  61. #else
  62. public static AstarPath active;
  63. #endif
  64. /// <summary>Shortcut to Pathfinding.AstarData.graphs</summary>
  65. public NavGraph[] graphs {
  66. get {
  67. if (data == null)
  68. data = new AstarData();
  69. return data.graphs;
  70. }
  71. }
  72. #region InspectorDebug
  73. /// <summary>
  74. /// Visualize graphs in the scene view (editor only).
  75. /// [Open online documentation to see images]
  76. /// </summary>
  77. public bool showNavGraphs = true;
  78. /// <summary>
  79. /// Toggle to show unwalkable nodes.
  80. ///
  81. /// Note: Only relevant in the editor
  82. ///
  83. /// See: <see cref="unwalkableNodeDebugSize"/>
  84. /// </summary>
  85. public bool showUnwalkableNodes = true;
  86. /// <summary>
  87. /// The mode to use for drawing nodes in the sceneview.
  88. ///
  89. /// Note: Only relevant in the editor
  90. ///
  91. /// See: Pathfinding.GraphDebugMode
  92. /// </summary>
  93. public GraphDebugMode debugMode;
  94. /// <summary>
  95. /// Low value to use for certain <see cref="debugMode"/> modes.
  96. /// For example if <see cref="debugMode"/> is set to G, this value will determine when the node will be completely red.
  97. ///
  98. /// Note: Only relevant in the editor
  99. ///
  100. /// See: <see cref="debugRoof"/>
  101. /// See: <see cref="debugMode"/>
  102. /// </summary>
  103. public float debugFloor = 0;
  104. /// <summary>
  105. /// High value to use for certain <see cref="debugMode"/> modes.
  106. /// For example if <see cref="debugMode"/> is set to G, this value will determine when the node will be completely green.
  107. ///
  108. /// For the penalty debug mode, the nodes will be colored green when they have a penalty less than <see cref="debugFloor"/> and red
  109. /// when their penalty is greater or equal to this value and something between red and green otherwise.
  110. ///
  111. /// Note: Only relevant in the editor
  112. ///
  113. /// See: <see cref="debugFloor"/>
  114. /// See: <see cref="debugMode"/>
  115. /// </summary>
  116. public float debugRoof = 20000;
  117. /// <summary>
  118. /// If set, the <see cref="debugFloor"/> and <see cref="debugRoof"/> values will not be automatically recalculated.
  119. ///
  120. /// Note: Only relevant in the editor
  121. /// </summary>
  122. public bool manualDebugFloorRoof = false;
  123. /// <summary>
  124. /// If enabled, nodes will draw a line to their 'parent'.
  125. /// This will show the search tree for the latest path.
  126. ///
  127. /// Note: Only relevant in the editor
  128. ///
  129. /// TODO: Add a showOnlyLastPath flag to indicate whether to draw every node or only the ones visited by the latest path.
  130. /// </summary>
  131. public bool showSearchTree = false;
  132. /// <summary>
  133. /// Size of the red cubes shown in place of unwalkable nodes.
  134. ///
  135. /// Note: Only relevant in the editor. Does not apply to grid graphs.
  136. /// See: <see cref="showUnwalkableNodes"/>
  137. /// </summary>
  138. public float unwalkableNodeDebugSize = 0.3F;
  139. /// <summary>
  140. /// The amount of debugging messages.
  141. /// Use less debugging to improve performance (a bit) or just to get rid of the Console spamming.
  142. /// Use more debugging (heavy) if you want more information about what the pathfinding scripts are doing.
  143. /// The InGame option will display the latest path log using in-game GUI.
  144. ///
  145. /// [Open online documentation to see images]
  146. /// </summary>
  147. public PathLog logPathResults = PathLog.Normal;
  148. #endregion
  149. #region InspectorSettings
  150. /// <summary>
  151. /// Maximum distance to search for nodes.
  152. /// When searching for the nearest node to a point, this is the limit (in world units) for how far away it is allowed to be.
  153. ///
  154. /// This is relevant if you try to request a path to a point that cannot be reached and it thus has to search for
  155. /// the closest node to that point which can be reached (which might be far away). If it cannot find a node within this distance
  156. /// then the path will fail.
  157. ///
  158. /// [Open online documentation to see images]
  159. ///
  160. /// See: Pathfinding.NNConstraint.constrainDistance
  161. /// </summary>
  162. public float maxNearestNodeDistance = 100;
  163. /// <summary>
  164. /// Max Nearest Node Distance Squared.
  165. /// See: <see cref="maxNearestNodeDistance"/>
  166. /// </summary>
  167. public float maxNearestNodeDistanceSqr {
  168. get { return maxNearestNodeDistance*maxNearestNodeDistance; }
  169. }
  170. /// <summary>
  171. /// If true, all graphs will be scanned during Awake.
  172. /// If you disable this, you will have to call <see cref="Scan"/> yourself to enable pathfinding.
  173. /// Alternatively you could load a saved graph from a file.
  174. ///
  175. /// If a startup cache has been generated (see save-load-graphs) (view in online documentation for working links), it always takes priority to load that instead of scanning the graphs.
  176. ///
  177. /// This can be useful to enable if you want to scan your graphs asynchronously, or if you have a procedural world which has not been created yet
  178. /// at the start of the game.
  179. ///
  180. /// See: <see cref="Scan"/>
  181. /// See: <see cref="ScanAsync"/>
  182. /// </summary>
  183. public bool scanOnStartup = true;
  184. /// <summary>
  185. /// Do a full GetNearest search for all graphs.
  186. /// Additional searches will normally only be done on the graph which in the first fast search seemed to have the closest node.
  187. /// With this setting on, additional searches will be done on all graphs since the first check is not always completely accurate.
  188. /// More technically: GetNearestForce on all graphs will be called if true, otherwise only on the one graph which's GetNearest search returned the best node.
  189. /// Usually faster when disabled, but higher quality searches when enabled.
  190. /// Note: For the PointGraph this setting doesn't matter much as it has only one search mode.
  191. /// </summary>
  192. public bool fullGetNearestSearch = false;
  193. /// <summary>
  194. /// Prioritize graphs.
  195. /// Graphs will be prioritized based on their order in the inspector.
  196. /// The first graph which has a node closer than <see cref="prioritizeGraphsLimit"/> will be chosen instead of searching all graphs.
  197. ///
  198. /// Deprecated: This setting is discouraged, and it will be removed in a future update.
  199. /// </summary>
  200. [System.Obsolete("This setting is discouraged, and it will be removed in a future update")]
  201. public bool prioritizeGraphs = false;
  202. /// <summary>
  203. /// Distance limit for <see cref="prioritizeGraphs"/>.
  204. /// See: <see cref="prioritizeGraphs"/>
  205. ///
  206. /// Deprecated: This setting is discouraged, and it will be removed in a future update.
  207. /// </summary>
  208. [System.Obsolete("This setting is discouraged, and it will be removed in a future update")]
  209. public float prioritizeGraphsLimit = 1F;
  210. /// <summary>
  211. /// Reference to the color settings for this AstarPath object.
  212. /// Color settings include for example which color the nodes should be in, in the sceneview.
  213. /// </summary>
  214. public AstarColor colorSettings;
  215. /// <summary>
  216. /// Stored tag names.
  217. /// See: AstarPath.FindTagNames
  218. /// See: AstarPath.GetTagNames
  219. /// </summary>
  220. [SerializeField]
  221. protected string[] tagNames = null;
  222. /// <summary>
  223. /// The distance function to use as a heuristic.
  224. /// The heuristic, often referred to as just 'H' is the estimated cost from a node to the target.
  225. /// Different heuristics affect how the path picks which one to follow from multiple possible with the same length
  226. /// See: <see cref="Pathfinding.Heuristic"/> for more details and descriptions of the different modes.
  227. /// See: <a href="https://en.wikipedia.org/wiki/Admissible_heuristic">Wikipedia: Admissible heuristic</a>
  228. /// See: <a href="https://en.wikipedia.org/wiki/A*_search_algorithm">Wikipedia: A* search algorithm</a>
  229. /// See: <a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">Wikipedia: Dijkstra's Algorithm</a>
  230. /// </summary>
  231. public Heuristic heuristic = Heuristic.Euclidean;
  232. /// <summary>
  233. /// The scale of the heuristic.
  234. /// If a value lower than 1 is used, the pathfinder will search more nodes (slower).
  235. /// If 0 is used, the pathfinding algorithm will be reduced to dijkstra's algorithm. This is equivalent to setting <see cref="heuristic"/> to None.
  236. /// If a value larger than 1 is used the pathfinding will (usually) be faster because it expands fewer nodes, but the paths may no longer be the optimal (i.e the shortest possible paths).
  237. ///
  238. /// Usually you should leave this to the default value of 1.
  239. ///
  240. /// See: https://en.wikipedia.org/wiki/Admissible_heuristic
  241. /// See: https://en.wikipedia.org/wiki/A*_search_algorithm
  242. /// See: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
  243. /// </summary>
  244. public float heuristicScale = 1F;
  245. /// <summary>
  246. /// Number of pathfinding threads to use.
  247. /// Multithreading puts pathfinding in another thread, this is great for performance on 2+ core computers since the framerate will barely be affected by the pathfinding at all.
  248. /// - None indicates that the pathfinding is run in the Unity thread as a coroutine
  249. /// - Automatic will try to adjust the number of threads to the number of cores and memory on the computer.
  250. /// Less than 512mb of memory or a single core computer will make it revert to using no multithreading.
  251. ///
  252. /// It is recommended that you use one of the "Auto" settings that are available.
  253. /// The reason is that even if your computer might be beefy and have 8 cores.
  254. /// Other computers might only be quad core or dual core in which case they will not benefit from more than
  255. /// 1 or 3 threads respectively (you usually want to leave one core for the unity thread).
  256. /// If you use more threads than the number of cores on the computer it is mostly just wasting memory, it will not run any faster.
  257. /// The extra memory usage is not trivially small. Each thread needs to keep a small amount of data for each node in all the graphs.
  258. /// It is not the full graph data but it is proportional to the number of nodes.
  259. /// The automatic settings will inspect the machine it is running on and use that to determine the number of threads so that no memory is wasted.
  260. ///
  261. /// The exception is if you only have one (or maybe two characters) active at time. Then you should probably just go with one thread always since it is very unlikely
  262. /// that you will need the extra throughput given by more threads. Keep in mind that more threads primarily increases throughput by calculating different paths on different
  263. /// threads, it will not calculate individual paths any faster.
  264. ///
  265. /// Note that if you are modifying the pathfinding core scripts or if you are directly modifying graph data without using any of the
  266. /// safe wrappers (like <see cref="AddWorkItem)"/> multithreading can cause strange errors and pathfinding stopping to work if you are not careful.
  267. /// For basic usage (not modding the pathfinding core) it should be safe.
  268. ///
  269. /// Note: WebGL does not support threads at all (since javascript is single-threaded) so no threads will be used on that platform.
  270. ///
  271. /// See: CalculateThreadCount
  272. /// </summary>
  273. public ThreadCount threadCount = ThreadCount.One;
  274. /// <summary>
  275. /// Max number of milliseconds to spend each frame for pathfinding.
  276. /// At least 500 nodes will be searched each frame (if there are that many to search).
  277. /// When using multithreading this value is irrelevant.
  278. /// </summary>
  279. public float maxFrameTime = 1F;
  280. /// <summary>
  281. /// Throttle graph updates and batch them to improve performance.
  282. /// If toggled, graph updates will batched and executed less often (specified by <see cref="graphUpdateBatchingInterval)"/>.
  283. ///
  284. /// This can have a positive impact on pathfinding throughput since the pathfinding threads do not need
  285. /// to be stopped as often, and it reduces the overhead per graph update.
  286. /// All graph updates are still applied however, they are just batched together so that more of them are
  287. /// applied at the same time.
  288. ///
  289. /// However do not use this if you want minimal latency between a graph update being requested
  290. /// and it being applied.
  291. ///
  292. /// This only applies to graph updates requested using the <see cref="UpdateGraphs"/> method. Not those requested
  293. /// using <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
  294. ///
  295. /// If you want to apply graph updates immediately at some point, you can call <see cref="FlushGraphUpdates"/>.
  296. ///
  297. /// See: graph-updates (view in online documentation for working links)
  298. /// </summary>
  299. public bool batchGraphUpdates = false;
  300. /// <summary>
  301. /// Minimum number of seconds between each batch of graph updates.
  302. /// If <see cref="batchGraphUpdates"/> is true, this defines the minimum number of seconds between each batch of graph updates.
  303. ///
  304. /// This can have a positive impact on pathfinding throughput since the pathfinding threads do not need
  305. /// to be stopped as often, and it reduces the overhead per graph update.
  306. /// All graph updates are still applied however, they are just batched together so that more of them are
  307. /// applied at the same time.
  308. ///
  309. /// Do not use this if you want minimal latency between a graph update being requested
  310. /// and it being applied.
  311. ///
  312. /// This only applies to graph updates requested using the <see cref="UpdateGraphs"/> method. Not those requested
  313. /// using <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
  314. ///
  315. /// See: graph-updates (view in online documentation for working links)
  316. /// </summary>
  317. public float graphUpdateBatchingInterval = 0.2F;
  318. /// <summary>
  319. /// Batch graph updates.
  320. /// Deprecated: This field has been renamed to <see cref="batchGraphUpdates"/>.
  321. /// </summary>
  322. [System.Obsolete("This field has been renamed to 'batchGraphUpdates'")]
  323. public bool limitGraphUpdates { get { return batchGraphUpdates; } set { batchGraphUpdates = value; } }
  324. /// <summary>
  325. /// Limit for how often should graphs be updated.
  326. /// Deprecated: This field has been renamed to <see cref="graphUpdateBatchingInterval"/>.
  327. /// </summary>
  328. [System.Obsolete("This field has been renamed to 'graphUpdateBatchingInterval'")]
  329. public float maxGraphUpdateFreq { get { return graphUpdateBatchingInterval; } set { graphUpdateBatchingInterval = value; } }
  330. #endregion
  331. #region DebugVariables
  332. #if ProfileAstar
  333. /// <summary>
  334. /// How many paths has been computed this run. From application start.
  335. /// Debugging variable
  336. /// </summary>
  337. public static int PathsCompleted = 0;
  338. public static System.Int64 TotalSearchedNodes = 0;
  339. public static System.Int64 TotalSearchTime = 0;
  340. #endif
  341. /// <summary>
  342. /// The time it took for the last call to Scan() to complete.
  343. /// Used to prevent automatically rescanning the graphs too often (editor only)
  344. /// </summary>
  345. public float lastScanTime { get; private set; }
  346. /// <summary>
  347. /// The path to debug using gizmos.
  348. /// This is the path handler used to calculate the last path.
  349. /// It is used in the editor to draw debug information using gizmos.
  350. /// </summary>
  351. [System.NonSerialized]
  352. public PathHandler debugPathData;
  353. /// <summary>The path ID to debug using gizmos</summary>
  354. [System.NonSerialized]
  355. public ushort debugPathID;
  356. /// <summary>
  357. /// Debug string from the last completed path.
  358. /// Will be updated if <see cref="logPathResults"/> == PathLog.InGame
  359. /// </summary>
  360. string inGameDebugPath;
  361. #endregion
  362. #region StatusVariables
  363. /// <summary>
  364. /// Backing field for <see cref="isScanning"/>.
  365. /// Cannot use an auto-property because they cannot be marked with System.NonSerialized.
  366. /// </summary>
  367. [System.NonSerialized]
  368. bool isScanningBacking;
  369. /// <summary>
  370. /// Set while any graphs are being scanned.
  371. /// It will be true up until the FloodFill is done.
  372. ///
  373. /// Note: Not to be confused with graph updates.
  374. ///
  375. /// Used to better support Graph Update Objects called for example in OnPostScan
  376. ///
  377. /// See: IsAnyGraphUpdateQueued
  378. /// See: IsAnyGraphUpdateInProgress
  379. /// </summary>
  380. public bool isScanning { get { return isScanningBacking; } private set { isScanningBacking = value; } }
  381. /// <summary>
  382. /// Number of parallel pathfinders.
  383. /// Returns the number of concurrent processes which can calculate paths at once.
  384. /// When using multithreading, this will be the number of threads, if not using multithreading it is always 1 (since only 1 coroutine is used).
  385. /// See: IsUsingMultithreading
  386. /// </summary>
  387. public int NumParallelThreads {
  388. get {
  389. return pathProcessor.NumThreads;
  390. }
  391. }
  392. /// <summary>
  393. /// Returns whether or not multithreading is used.
  394. /// \exception System.Exception Is thrown when it could not be decided if multithreading was used or not.
  395. /// This should not happen if pathfinding is set up correctly.
  396. /// Note: This uses info about if threads are running right now, it does not use info from the settings on the A* object.
  397. /// </summary>
  398. public bool IsUsingMultithreading {
  399. get {
  400. return pathProcessor.IsUsingMultithreading;
  401. }
  402. }
  403. /// <summary>
  404. /// Returns if any graph updates are waiting to be applied.
  405. /// Deprecated: Use IsAnyGraphUpdateQueued instead
  406. /// </summary>
  407. [System.Obsolete("Fixed grammar, use IsAnyGraphUpdateQueued instead")]
  408. public bool IsAnyGraphUpdatesQueued { get { return IsAnyGraphUpdateQueued; } }
  409. /// <summary>
  410. /// Returns if any graph updates are waiting to be applied.
  411. /// Note: This is false while the updates are being performed.
  412. /// Note: This does *not* includes other types of work items such as navmesh cutting or anything added by <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
  413. /// </summary>
  414. public bool IsAnyGraphUpdateQueued { get { return graphUpdates.IsAnyGraphUpdateQueued; } }
  415. /// <summary>
  416. /// Returns if any graph updates are being calculated right now.
  417. /// Note: This does *not* includes other types of work items such as navmesh cutting or anything added by <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
  418. ///
  419. /// See: IsAnyWorkItemInProgress
  420. /// </summary>
  421. public bool IsAnyGraphUpdateInProgress { get { return graphUpdates.IsAnyGraphUpdateInProgress; } }
  422. /// <summary>
  423. /// Returns if any work items are in progress right now.
  424. /// Note: This includes pretty much all types of graph updates.
  425. /// Such as normal graph updates, navmesh cutting and anything added by <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
  426. /// </summary>
  427. public bool IsAnyWorkItemInProgress { get { return workItems.workItemsInProgress; } }
  428. /// <summary>
  429. /// Returns if this code is currently being exectuted inside a work item.
  430. /// Note: This includes pretty much all types of graph updates.
  431. /// Such as normal graph updates, navmesh cutting and anything added by <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
  432. ///
  433. /// In contrast to <see cref="IsAnyWorkItemInProgress"/> this is only true when work item code is being executed, it is not
  434. /// true in-between the updates to a work item that takes several frames to complete.
  435. /// </summary>
  436. internal bool IsInsideWorkItem { get { return workItems.workItemsInProgressRightNow; } }
  437. #endregion
  438. #region Callbacks
  439. /// <summary>
  440. /// Called on Awake before anything else is done.
  441. /// This is called at the start of the Awake call, right after <see cref="active"/> has been set, but this is the only thing that has been done.
  442. /// Use this when you want to set up default settings for an AstarPath component created during runtime since some settings can only be changed in Awake
  443. /// (such as multithreading related stuff)
  444. /// <code>
  445. /// // Create a new AstarPath object on Start and apply some default settings
  446. /// public void Start () {
  447. /// AstarPath.OnAwakeSettings += ApplySettings;
  448. /// AstarPath astar = gameObject.AddComponent<AstarPath>();
  449. /// }
  450. ///
  451. /// public void ApplySettings () {
  452. /// // Unregister from the delegate
  453. /// AstarPath.OnAwakeSettings -= ApplySettings;
  454. /// // For example threadCount should not be changed after the Awake call
  455. /// // so here's the only place to set it if you create the component during runtime
  456. /// AstarPath.active.threadCount = ThreadCount.One;
  457. /// }
  458. /// </code>
  459. /// </summary>
  460. public static System.Action OnAwakeSettings;
  461. /// <summary>Called for each graph before they are scanned</summary>
  462. public static OnGraphDelegate OnGraphPreScan;
  463. /// <summary>Called for each graph after they have been scanned. All other graphs might not have been scanned yet.</summary>
  464. public static OnGraphDelegate OnGraphPostScan;
  465. /// <summary>Called for each path before searching. Be careful when using multithreading since this will be called from a different thread.</summary>
  466. public static OnPathDelegate OnPathPreSearch;
  467. /// <summary>Called for each path after searching. Be careful when using multithreading since this will be called from a different thread.</summary>
  468. public static OnPathDelegate OnPathPostSearch;
  469. /// <summary>Called before starting the scanning</summary>
  470. public static OnScanDelegate OnPreScan;
  471. /// <summary>Called after scanning. This is called before applying links, flood-filling the graphs and other post processing.</summary>
  472. public static OnScanDelegate OnPostScan;
  473. /// <summary>Called after scanning has completed fully. This is called as the last thing in the Scan function.</summary>
  474. public static OnScanDelegate OnLatePostScan;
  475. /// <summary>Called when any graphs are updated. Register to for example recalculate the path whenever a graph changes.</summary>
  476. public static OnScanDelegate OnGraphsUpdated;
  477. /// <summary>
  478. /// Called when pathID overflows 65536 and resets back to zero.
  479. /// Note: This callback will be cleared every time it is called, so if you want to register to it repeatedly, register to it directly on receiving the callback as well.
  480. /// </summary>
  481. public static System.Action On65KOverflow;
  482. /// <summary>Deprecated:</summary>
  483. [System.ObsoleteAttribute]
  484. public System.Action OnGraphsWillBeUpdated;
  485. /// <summary>Deprecated:</summary>
  486. [System.ObsoleteAttribute]
  487. public System.Action OnGraphsWillBeUpdated2;
  488. #endregion
  489. #region MemoryStructures
  490. /// <summary>Processes graph updates</summary>
  491. readonly GraphUpdateProcessor graphUpdates;
  492. /// <summary>Holds a hierarchical graph to speed up some queries like if there is a path between two nodes</summary>
  493. internal readonly HierarchicalGraph hierarchicalGraph = new HierarchicalGraph();
  494. /// <summary>
  495. /// Handles navmesh cuts.
  496. /// See: <see cref="Pathfinding.NavmeshCut"/>
  497. /// </summary>
  498. public readonly NavmeshUpdates navmeshUpdates = new NavmeshUpdates();
  499. /// <summary>Processes work items</summary>
  500. readonly WorkItemProcessor workItems;
  501. /// <summary>Holds all paths waiting to be calculated and calculates them</summary>
  502. PathProcessor pathProcessor;
  503. bool graphUpdateRoutineRunning = false;
  504. /// <summary>Makes sure QueueGraphUpdates will not queue multiple graph update orders</summary>
  505. bool graphUpdatesWorkItemAdded = false;
  506. /// <summary>
  507. /// Time the last graph update was done.
  508. /// Used to group together frequent graph updates to batches
  509. /// </summary>
  510. float lastGraphUpdate = -9999F;
  511. /// <summary>Held if any work items are currently queued</summary>
  512. PathProcessor.GraphUpdateLock workItemLock;
  513. /// <summary>Holds all completed paths waiting to be returned to where they were requested</summary>
  514. internal readonly PathReturnQueue pathReturnQueue;
  515. /// <summary>
  516. /// Holds settings for heuristic optimization.
  517. /// See: heuristic-opt (view in online documentation for working links)
  518. /// </summary>
  519. public EuclideanEmbedding euclideanEmbedding = new EuclideanEmbedding();
  520. #endregion
  521. /// <summary>
  522. /// Shows or hides graph inspectors.
  523. /// Used internally by the editor
  524. /// </summary>
  525. public bool showGraphs = false;
  526. /// <summary>
  527. /// The next unused Path ID.
  528. /// Incremented for every call to GetNextPathID
  529. /// </summary>
  530. private ushort nextFreePathID = 1;
  531. private AstarPath () {
  532. pathReturnQueue = new PathReturnQueue(this);
  533. // Make sure that the pathProcessor is never null
  534. pathProcessor = new PathProcessor(this, pathReturnQueue, 1, false);
  535. workItems = new WorkItemProcessor(this);
  536. graphUpdates = new GraphUpdateProcessor(this);
  537. // Forward graphUpdates.OnGraphsUpdated to AstarPath.OnGraphsUpdated
  538. graphUpdates.OnGraphsUpdated += () => {
  539. if (OnGraphsUpdated != null) {
  540. OnGraphsUpdated(this);
  541. }
  542. };
  543. }
  544. /// <summary>
  545. /// Returns tag names.
  546. /// Makes sure that the tag names array is not null and of length 32.
  547. /// If it is null or not of length 32, it creates a new array and fills it with 0,1,2,3,4 etc...
  548. /// See: AstarPath.FindTagNames
  549. /// </summary>
  550. public string[] GetTagNames () {
  551. if (tagNames == null || tagNames.Length != 32) {
  552. tagNames = new string[32];
  553. for (int i = 0; i < tagNames.Length; i++) {
  554. tagNames[i] = ""+i;
  555. }
  556. tagNames[0] = "Basic Ground";
  557. }
  558. return tagNames;
  559. }
  560. /// <summary>
  561. /// Used outside of play mode to initialize the AstarPath object even if it has not been selected in the inspector yet.
  562. /// This will set the <see cref="active"/> property and deserialize all graphs.
  563. ///
  564. /// This is useful if you want to do changes to the graphs in the editor outside of play mode, but cannot be sure that the graphs have been deserialized yet.
  565. /// In play mode this method does nothing.
  566. /// </summary>
  567. public static void FindAstarPath () {
  568. if (Application.isPlaying) return;
  569. if (active == null) active = GameObject.FindObjectOfType<AstarPath>();
  570. if (active != null && (active.data.graphs == null || active.data.graphs.Length == 0)) active.data.DeserializeGraphs();
  571. }
  572. /// <summary>
  573. /// Tries to find an AstarPath object and return tag names.
  574. /// If an AstarPath object cannot be found, it returns an array of length 1 with an error message.
  575. /// See: AstarPath.GetTagNames
  576. /// </summary>
  577. public static string[] FindTagNames () {
  578. FindAstarPath();
  579. return active != null? active.GetTagNames () : new string[1] { "There is no AstarPath component in the scene" };
  580. }
  581. /// <summary>Returns the next free path ID</summary>
  582. internal ushort GetNextPathID () {
  583. if (nextFreePathID == 0) {
  584. nextFreePathID++;
  585. if (On65KOverflow != null) {
  586. System.Action tmp = On65KOverflow;
  587. On65KOverflow = null;
  588. tmp();
  589. }
  590. }
  591. return nextFreePathID++;
  592. }
  593. void RecalculateDebugLimits () {
  594. debugFloor = float.PositiveInfinity;
  595. debugRoof = float.NegativeInfinity;
  596. bool ignoreSearchTree = !showSearchTree || debugPathData == null;
  597. for (int i = 0; i < graphs.Length; i++) {
  598. if (graphs[i] != null && graphs[i].drawGizmos) {
  599. graphs[i].GetNodes(node => {
  600. if (node.Walkable && (ignoreSearchTree || Pathfinding.Util.GraphGizmoHelper.InSearchTree(node, debugPathData, debugPathID))) {
  601. if (debugMode == GraphDebugMode.Penalty) {
  602. debugFloor = Mathf.Min(debugFloor, node.Penalty);
  603. debugRoof = Mathf.Max(debugRoof, node.Penalty);
  604. } else if (debugPathData != null) {
  605. var rnode = debugPathData.GetPathNode(node);
  606. switch (debugMode) {
  607. case GraphDebugMode.F:
  608. debugFloor = Mathf.Min(debugFloor, rnode.F);
  609. debugRoof = Mathf.Max(debugRoof, rnode.F);
  610. break;
  611. case GraphDebugMode.G:
  612. debugFloor = Mathf.Min(debugFloor, rnode.G);
  613. debugRoof = Mathf.Max(debugRoof, rnode.G);
  614. break;
  615. case GraphDebugMode.H:
  616. debugFloor = Mathf.Min(debugFloor, rnode.H);
  617. debugRoof = Mathf.Max(debugRoof, rnode.H);
  618. break;
  619. }
  620. }
  621. }
  622. });
  623. }
  624. }
  625. if (float.IsInfinity(debugFloor)) {
  626. debugFloor = 0;
  627. debugRoof = 1;
  628. }
  629. // Make sure they are not identical, that will cause the color interpolation to fail
  630. if (debugRoof-debugFloor < 1) debugRoof += 1;
  631. }
  632. Pathfinding.Util.RetainedGizmos gizmos = new Pathfinding.Util.RetainedGizmos();
  633. /// <summary>Calls OnDrawGizmos on graph generators</summary>
  634. private void OnDrawGizmos () {
  635. // Make sure the singleton pattern holds
  636. // Might not hold if the Awake method
  637. // has not been called yet
  638. if (active == null) active = this;
  639. if (active != this || graphs == null) {
  640. return;
  641. }
  642. // In Unity one can select objects in the scene view by simply clicking on them with the mouse.
  643. // Graph gizmos interfere with this however. If we would draw a mesh here the user would
  644. // not be able to select whatever was behind it because the gizmos would block them.
  645. // (presumably Unity cannot associate the gizmos with the AstarPath component because we are using
  646. // Graphics.DrawMeshNow to draw most gizmos). It turns out that when scene picking happens
  647. // then Event.current.type will be 'mouseUp'. We will therefore ignore all events which are
  648. // not repaint events to make sure that the gizmos do not interfere with any kind of scene picking.
  649. // This will not have any visual impact as only repaint events will result in any changes on the screen.
  650. // From testing it seems the only events that can happen during OnDrawGizmos are the mouseUp and repaint events.
  651. if (Event.current.type != EventType.Repaint) return;
  652. colorSettings.PushToStatic(this);
  653. AstarProfiler.StartProfile("OnDrawGizmos");
  654. if (workItems.workItemsInProgress || isScanning) {
  655. // If updating graphs, graph info might not be valid right now
  656. // so just draw the same thing as last frame.
  657. // Also if the scene has multiple cameras (or in the editor if we have a scene view and a game view) we
  658. // just calculate the mesh once and then redraw the existing one for the other cameras.
  659. // This improves performance quite a bit.
  660. gizmos.DrawExisting();
  661. } else {
  662. if (showNavGraphs && !manualDebugFloorRoof) {
  663. RecalculateDebugLimits();
  664. }
  665. Profiler.BeginSample("Graph.OnDrawGizmos");
  666. // Loop through all graphs and draw their gizmos
  667. for (int i = 0; i < graphs.Length; i++) {
  668. if (graphs[i] != null && graphs[i].drawGizmos)
  669. graphs[i].OnDrawGizmos(gizmos, showNavGraphs);
  670. }
  671. Profiler.EndSample();
  672. if (showNavGraphs) {
  673. euclideanEmbedding.OnDrawGizmos();
  674. if (debugMode == GraphDebugMode.HierarchicalNode) hierarchicalGraph.OnDrawGizmos(gizmos);
  675. }
  676. }
  677. gizmos.FinalizeDraw();
  678. AstarProfiler.EndProfile("OnDrawGizmos");
  679. }
  680. #if !ASTAR_NO_GUI
  681. /// <summary>
  682. /// Draws the InGame debugging (if enabled), also shows the fps if 'L' is pressed down.
  683. /// See: <see cref="logPathResults"/> PathLog
  684. /// </summary>
  685. private void OnGUI () {
  686. if (logPathResults == PathLog.InGame && inGameDebugPath != "") {
  687. GUI.Label(new Rect(5, 5, 400, 600), inGameDebugPath);
  688. }
  689. }
  690. #endif
  691. /// <summary>
  692. /// Prints path results to the log. What it prints can be controled using <see cref="logPathResults"/>.
  693. /// See: <see cref="logPathResults"/>
  694. /// See: PathLog
  695. /// See: Pathfinding.Path.DebugString
  696. /// </summary>
  697. private void LogPathResults (Path path) {
  698. if (logPathResults != PathLog.None && (path.error || logPathResults != PathLog.OnlyErrors)) {
  699. string debug = (path as IPathInternals).DebugString(logPathResults);
  700. if (logPathResults == PathLog.InGame) {
  701. inGameDebugPath = debug;
  702. } else if (path.error) {
  703. Debug.LogWarning(debug);
  704. } else {
  705. Debug.Log(debug);
  706. }
  707. }
  708. }
  709. /// <summary>
  710. /// Checks if any work items need to be executed
  711. /// then runs pathfinding for a while (if not using multithreading because
  712. /// then the calculation happens in other threads)
  713. /// and then returns any calculated paths to the
  714. /// scripts that requested them.
  715. ///
  716. /// See: PerformBlockingActions
  717. /// See: PathProcessor.TickNonMultithreaded
  718. /// See: PathReturnQueue.ReturnPaths
  719. /// </summary>
  720. private void Update () {
  721. // This class uses the [ExecuteInEditMode] attribute
  722. // So Update is called even when not playing
  723. // Don't do anything when not in play mode
  724. if (!Application.isPlaying) return;
  725. navmeshUpdates.Update();
  726. // Execute blocking actions such as graph updates
  727. // when not scanning
  728. if (!isScanning) {
  729. PerformBlockingActions();
  730. }
  731. // Calculates paths when not using multithreading
  732. pathProcessor.TickNonMultithreaded();
  733. // Return calculated paths
  734. pathReturnQueue.ReturnPaths(true);
  735. }
  736. private void PerformBlockingActions (bool force = false) {
  737. if (workItemLock.Held && pathProcessor.queue.AllReceiversBlocked) {
  738. // Return all paths before starting blocking actions
  739. // since these might change the graph and make returned paths invalid (at least the nodes)
  740. pathReturnQueue.ReturnPaths(false);
  741. Profiler.BeginSample("Work Items");
  742. if (workItems.ProcessWorkItems(force)) {
  743. // At this stage there are no more work items, resume pathfinding threads
  744. workItemLock.Release();
  745. }
  746. Profiler.EndSample();
  747. }
  748. }
  749. /// <summary>
  750. /// Call during work items to queue a flood fill.
  751. /// Deprecated: This method has been moved. Use the method on the context object that can be sent with work item delegates instead
  752. /// <code>
  753. /// AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
  754. /// // Safe to update graphs here
  755. /// var node = AstarPath.active.GetNearest(transform.position).node;
  756. /// node.Walkable = false;
  757. /// }));
  758. /// </code>
  759. ///
  760. /// See: <see cref="Pathfinding.IWorkItemContext"/>
  761. /// </summary>
  762. [System.Obsolete("This method has been moved. Use the method on the context object that can be sent with work item delegates instead")]
  763. public void QueueWorkItemFloodFill () {
  764. throw new System.Exception("This method has been moved. Use the method on the context object that can be sent with work item delegates instead");
  765. }
  766. /// <summary>
  767. /// If a WorkItem needs to have a valid flood fill during execution, call this method to ensure there are no pending flood fills.
  768. /// Deprecated: This method has been moved. Use the method on the context object that can be sent with work item delegates instead
  769. /// <code>
  770. /// AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
  771. /// // Safe to update graphs here
  772. /// var node = AstarPath.active.GetNearest(transform.position).node;
  773. /// node.Walkable = false;
  774. /// }));
  775. /// </code>
  776. ///
  777. /// See: <see cref="Pathfinding.IWorkItemContext"/>
  778. /// </summary>
  779. [System.Obsolete("This method has been moved. Use the method on the context object that can be sent with work item delegates instead")]
  780. public void EnsureValidFloodFill () {
  781. throw new System.Exception("This method has been moved. Use the method on the context object that can be sent with work item delegates instead");
  782. }
  783. /// <summary>
  784. /// Add a work item to be processed when pathfinding is paused.
  785. /// Convenience method that is equivalent to
  786. /// <code>
  787. /// AddWorkItem(new AstarWorkItem(callback));
  788. /// </code>
  789. ///
  790. /// See: <see cref="AddWorkItem(AstarWorkItem)"/>
  791. /// </summary>
  792. public void AddWorkItem (System.Action callback) {
  793. AddWorkItem(new AstarWorkItem(callback));
  794. }
  795. /// <summary>
  796. /// Add a work item to be processed when pathfinding is paused.
  797. /// Convenience method that is equivalent to
  798. /// <code>
  799. /// AddWorkItem(new AstarWorkItem(callback));
  800. /// </code>
  801. ///
  802. /// See: <see cref="AddWorkItem(AstarWorkItem)"/>
  803. /// </summary>
  804. public void AddWorkItem (System.Action<IWorkItemContext> callback) {
  805. AddWorkItem(new AstarWorkItem(callback));
  806. }
  807. /// <summary>
  808. /// Add a work item to be processed when pathfinding is paused.
  809. ///
  810. /// The work item will be executed when it is safe to update nodes. This is defined as between the path searches.
  811. /// When using more threads than one, calling this often might decrease pathfinding performance due to a lot of idling in the threads.
  812. /// Not performance as in it will use much CPU power, but performance as in the number of paths per second will probably go down
  813. /// (though your framerate might actually increase a tiny bit).
  814. ///
  815. /// You should only call this function from the main unity thread (i.e normal game code).
  816. ///
  817. /// <code>
  818. /// AstarPath.active.AddWorkItem(new AstarWorkItem(() => {
  819. /// // Safe to update graphs here
  820. /// var node = AstarPath.active.GetNearest(transform.position).node;
  821. /// node.Walkable = false;
  822. /// }));
  823. /// </code>
  824. ///
  825. /// <code>
  826. /// AstarPath.active.AddWorkItem(() => {
  827. /// // Safe to update graphs here
  828. /// var node = AstarPath.active.GetNearest(transform.position).node;
  829. /// node.position = (Int3)transform.position;
  830. /// });
  831. /// </code>
  832. ///
  833. /// See: <see cref="FlushWorkItems"/>
  834. /// </summary>
  835. public void AddWorkItem (AstarWorkItem item) {
  836. workItems.AddWorkItem(item);
  837. // Make sure pathfinding is stopped and work items are processed
  838. if (!workItemLock.Held) {
  839. workItemLock = PausePathfindingSoon();
  840. }
  841. #if UNITY_EDITOR
  842. // If not playing, execute instantly
  843. if (!Application.isPlaying) {
  844. FlushWorkItems();
  845. }
  846. #endif
  847. }
  848. #region GraphUpdateMethods
  849. /// <summary>
  850. /// Will apply queued graph updates as soon as possible, regardless of <see cref="batchGraphUpdates"/>.
  851. /// Calling this multiple times will not create multiple callbacks.
  852. /// This function is useful if you are limiting graph updates, but you want a specific graph update to be applied as soon as possible regardless of the time limit.
  853. /// Note that this does not block until the updates are done, it merely bypasses the <see cref="batchGraphUpdates"/> time limit.
  854. ///
  855. /// See: <see cref="FlushGraphUpdates"/>
  856. /// </summary>
  857. public void QueueGraphUpdates () {
  858. if (!graphUpdatesWorkItemAdded) {
  859. graphUpdatesWorkItemAdded = true;
  860. var workItem = graphUpdates.GetWorkItem();
  861. // Add a new work item which first
  862. // sets the graphUpdatesWorkItemAdded flag to false
  863. // and then processes the graph updates
  864. AddWorkItem(new AstarWorkItem(() => {
  865. graphUpdatesWorkItemAdded = false;
  866. lastGraphUpdate = Time.realtimeSinceStartup;
  867. workItem.init();
  868. }, workItem.update));
  869. }
  870. }
  871. /// <summary>
  872. /// Waits a moment with updating graphs.
  873. /// If batchGraphUpdates is set, we want to keep some space between them to let pathfinding threads running and then calculate all queued calls at once
  874. /// </summary>
  875. IEnumerator DelayedGraphUpdate () {
  876. graphUpdateRoutineRunning = true;
  877. yield return new WaitForSeconds(graphUpdateBatchingInterval-(Time.realtimeSinceStartup-lastGraphUpdate));
  878. QueueGraphUpdates();
  879. graphUpdateRoutineRunning = false;
  880. }
  881. /// <summary>
  882. /// Update all graphs within bounds after delay seconds.
  883. /// The graphs will be updated as soon as possible.
  884. ///
  885. /// See: FlushGraphUpdates
  886. /// See: batchGraphUpdates
  887. /// See: graph-updates (view in online documentation for working links)
  888. /// </summary>
  889. public void UpdateGraphs (Bounds bounds, float delay) {
  890. UpdateGraphs(new GraphUpdateObject(bounds), delay);
  891. }
  892. /// <summary>
  893. /// Update all graphs using the GraphUpdateObject after delay seconds.
  894. /// This can be used to, e.g make all nodes in a region unwalkable, or set them to a higher penalty.
  895. ///
  896. /// See: FlushGraphUpdates
  897. /// See: batchGraphUpdates
  898. /// See: graph-updates (view in online documentation for working links)
  899. /// </summary>
  900. public void UpdateGraphs (GraphUpdateObject ob, float delay) {
  901. StartCoroutine(UpdateGraphsInternal(ob, delay));
  902. }
  903. /// <summary>Update all graphs using the GraphUpdateObject after delay seconds</summary>
  904. IEnumerator UpdateGraphsInternal (GraphUpdateObject ob, float delay) {
  905. yield return new WaitForSeconds(delay);
  906. UpdateGraphs(ob);
  907. }
  908. /// <summary>
  909. /// Update all graphs within bounds.
  910. /// The graphs will be updated as soon as possible.
  911. ///
  912. /// This is equivalent to
  913. /// <code>
  914. /// UpdateGraphs(new GraphUpdateObject(bounds));
  915. /// </code>
  916. ///
  917. /// See: FlushGraphUpdates
  918. /// See: batchGraphUpdates
  919. /// See: graph-updates (view in online documentation for working links)
  920. /// </summary>
  921. public void UpdateGraphs (Bounds bounds) {
  922. UpdateGraphs(new GraphUpdateObject(bounds));
  923. }
  924. /// <summary>
  925. /// Update all graphs using the GraphUpdateObject.
  926. /// This can be used to, e.g make all nodes in a region unwalkable, or set them to a higher penalty.
  927. /// The graphs will be updated as soon as possible (with respect to <see cref="batchGraphUpdates)"/>
  928. ///
  929. /// See: FlushGraphUpdates
  930. /// See: batchGraphUpdates
  931. /// See: graph-updates (view in online documentation for working links)
  932. /// </summary>
  933. public void UpdateGraphs (GraphUpdateObject ob) {
  934. if (ob.internalStage != GraphUpdateObject.STAGE_CREATED) {
  935. throw new System.Exception("You are trying to update graphs using the same graph update object twice. Please create a new GraphUpdateObject instead.");
  936. }
  937. ob.internalStage = GraphUpdateObject.STAGE_PENDING;
  938. graphUpdates.AddToQueue(ob);
  939. // If we should limit graph updates, start a coroutine which waits until we should update graphs
  940. if (batchGraphUpdates && Time.realtimeSinceStartup-lastGraphUpdate < graphUpdateBatchingInterval) {
  941. if (!graphUpdateRoutineRunning) {
  942. StartCoroutine(DelayedGraphUpdate());
  943. }
  944. } else {
  945. // Otherwise, graph updates should be carried out as soon as possible
  946. QueueGraphUpdates();
  947. }
  948. }
  949. /// <summary>
  950. /// Forces graph updates to complete in a single frame.
  951. /// This will force the pathfinding threads to finish calculating the path they are currently calculating (if any) and then pause.
  952. /// When all threads have paused, graph updates will be performed.
  953. /// Warning: Using this very often (many times per second) can reduce your fps due to a lot of threads waiting for one another.
  954. /// But you probably wont have to worry about that.
  955. ///
  956. /// Note: This is almost identical to <see cref="FlushWorkItems"/>, but added for more descriptive name.
  957. /// This function will also override any time limit delays for graph updates.
  958. /// This is because graph updates are implemented using work items.
  959. /// So calling this function will also execute any other work items (if any are queued).
  960. ///
  961. /// Will not do anything if there are no graph updates queued (not even execute other work items).
  962. /// </summary>
  963. public void FlushGraphUpdates () {
  964. if (IsAnyGraphUpdateQueued) {
  965. QueueGraphUpdates();
  966. FlushWorkItems();
  967. }
  968. }
  969. #endregion
  970. /// <summary>
  971. /// Forces work items to complete in a single frame.
  972. /// This will force all work items to run immidiately.
  973. /// This will force the pathfinding threads to finish calculating the path they are currently calculating (if any) and then pause.
  974. /// When all threads have paused, work items will be executed (which can be e.g graph updates).
  975. ///
  976. /// Warning: Using this very often (many times per second) can reduce your fps due to a lot of threads waiting for one another.
  977. /// But you probably wont have to worry about that
  978. ///
  979. /// Note: This is almost (note almost) identical to <see cref="FlushGraphUpdates"/>, but added for more descriptive name.
  980. ///
  981. /// Will not do anything if there are no queued work items waiting to run.
  982. /// </summary>
  983. public void FlushWorkItems () {
  984. if (workItems.anyQueued) {
  985. var graphLock = PausePathfinding();
  986. PerformBlockingActions(true);
  987. graphLock.Release();
  988. }
  989. }
  990. /// <summary>
  991. /// Make sure work items are executed.
  992. ///
  993. /// See: AddWorkItem
  994. ///
  995. /// Deprecated: Use <see cref="FlushWorkItems()"/> instead.
  996. /// </summary>
  997. /// <param name="unblockOnComplete">If true, pathfinding will be allowed to start running immediately after completing all work items.</param>
  998. /// <param name="block">If true, work items that usually take more than one frame to complete will be forced to complete during this call.
  999. /// If false, then after this call there might still be work left to do.</param>
  1000. [System.Obsolete("Use FlushWorkItems() instead")]
  1001. public void FlushWorkItems (bool unblockOnComplete, bool block) {
  1002. var graphLock = PausePathfinding();
  1003. // Run tasks
  1004. PerformBlockingActions(block);
  1005. graphLock.Release();
  1006. }
  1007. /// <summary>
  1008. /// Forces thread safe callbacks to run.
  1009. /// Deprecated: Use <see cref="FlushWorkItems"/> instead
  1010. /// </summary>
  1011. [System.Obsolete("Use FlushWorkItems instead")]
  1012. public void FlushThreadSafeCallbacks () {
  1013. FlushWorkItems();
  1014. }
  1015. /// <summary>
  1016. /// Calculates number of threads to use.
  1017. /// If count is not Automatic, simply returns count casted to an int.
  1018. /// Returns: An int specifying how many threads to use, 0 means a coroutine should be used for pathfinding instead of a separate thread.
  1019. ///
  1020. /// If count is set to Automatic it will return a value based on the number of processors and memory for the current system.
  1021. /// If memory is <= 512MB or logical cores are <= 1, it will return 0. If memory is <= 1024 it will clamp threads to max 2.
  1022. /// Otherwise it will return the number of logical cores clamped to 6.
  1023. ///
  1024. /// When running on WebGL this method always returns 0
  1025. /// </summary>
  1026. public static int CalculateThreadCount (ThreadCount count) {
  1027. #if UNITY_WEBGL
  1028. return 0;
  1029. #else
  1030. if (count == ThreadCount.AutomaticLowLoad || count == ThreadCount.AutomaticHighLoad) {
  1031. #if ASTARDEBUG
  1032. Debug.Log(SystemInfo.systemMemorySize + " " + SystemInfo.processorCount + " " + SystemInfo.processorType);
  1033. #endif
  1034. int logicalCores = Mathf.Max(1, SystemInfo.processorCount);
  1035. int memory = SystemInfo.systemMemorySize;
  1036. if (memory <= 0) {
  1037. Debug.LogError("Machine reporting that is has <= 0 bytes of RAM. This is definitely not true, assuming 1 GiB");
  1038. memory = 1024;
  1039. }
  1040. if (logicalCores <= 1) return 0;
  1041. if (memory <= 512) return 0;
  1042. if (count == ThreadCount.AutomaticHighLoad) {
  1043. if (memory <= 1024) logicalCores = System.Math.Min(logicalCores, 2);
  1044. } else {
  1045. //Always run at at most processorCount-1 threads (one core reserved for unity thread).
  1046. // Many computers use hyperthreading, so dividing by two is used to remove the hyperthreading cores, pathfinding
  1047. // doesn't scale well past the number of physical cores anyway
  1048. logicalCores /= 2;
  1049. logicalCores = Mathf.Max(1, logicalCores);
  1050. if (memory <= 1024) logicalCores = System.Math.Min(logicalCores, 2);
  1051. logicalCores = System.Math.Min(logicalCores, 6);
  1052. }
  1053. return logicalCores;
  1054. } else {
  1055. int val = (int)count;
  1056. return val;
  1057. }
  1058. #endif
  1059. }
  1060. /// <summary>
  1061. /// Sets up all needed variables and scans the graphs.
  1062. /// Calls Initialize, starts the ReturnPaths coroutine and scans all graphs.
  1063. /// Also starts threads if using multithreading
  1064. /// See: <see cref="OnAwakeSettings"/>
  1065. /// </summary>
  1066. protected override void Awake () {
  1067. base.Awake();
  1068. if (active != null && active != this && Application.isPlaying) {
  1069. if (this.enabled) {
  1070. Debug.LogWarning("Another A* component is already in the scene. More than one A* component cannot be active at the same time. Disabling this one.", this);
  1071. }
  1072. enabled = false;
  1073. return;
  1074. }
  1075. // Very important to set this. Ensures the singleton pattern holds
  1076. active = this;
  1077. if (FindObjectsOfType(typeof(AstarPath)).Length > 1) {
  1078. Debug.LogError("You should NOT have more than one AstarPath component in the scene at any time.\n" +
  1079. "This can cause serious errors since the AstarPath component builds around a singleton pattern.", this);
  1080. }
  1081. // Disable GUILayout to gain some performance, it is not used in the OnGUI call
  1082. useGUILayout = false;
  1083. // This class uses the [ExecuteInEditMode] attribute
  1084. // So Awake is called even when not playing
  1085. // Don't do anything when not in play mode
  1086. if (!Application.isPlaying) return;
  1087. if (OnAwakeSettings != null) {
  1088. OnAwakeSettings();
  1089. }
  1090. // To make sure all graph modifiers have been enabled before scan (to avoid script execution order issues)
  1091. GraphModifier.FindAllModifiers();
  1092. RelevantGraphSurface.FindAllGraphSurfaces();
  1093. InitializePathProcessor();
  1094. InitializeProfiler();
  1095. ConfigureReferencesInternal();
  1096. InitializeAstarData();
  1097. // Flush work items, possibly added in InitializeAstarData to load graph data
  1098. FlushWorkItems();
  1099. euclideanEmbedding.dirty = true;
  1100. navmeshUpdates.OnEnable();
  1101. if (scanOnStartup && (!data.cacheStartup || data.file_cachedStartup == null)) {
  1102. Scan();
  1103. }
  1104. }
  1105. /// <summary>Initializes the <see cref="pathProcessor"/> field</summary>
  1106. void InitializePathProcessor () {
  1107. int numThreads = CalculateThreadCount(threadCount);
  1108. // Outside of play mode everything is synchronous, so no threads are used.
  1109. if (!Application.isPlaying) numThreads = 0;
  1110. int numProcessors = Mathf.Max(numThreads, 1);
  1111. bool multithreaded = numThreads > 0;
  1112. pathProcessor = new PathProcessor(this, pathReturnQueue, numProcessors, multithreaded);
  1113. pathProcessor.OnPathPreSearch += path => {
  1114. var tmp = OnPathPreSearch;
  1115. if (tmp != null) tmp(path);
  1116. };
  1117. pathProcessor.OnPathPostSearch += path => {
  1118. LogPathResults(path);
  1119. var tmp = OnPathPostSearch;
  1120. if (tmp != null) tmp(path);
  1121. };
  1122. // Sent every time the path queue is unblocked
  1123. pathProcessor.OnQueueUnblocked += () => {
  1124. if (euclideanEmbedding.dirty) {
  1125. euclideanEmbedding.RecalculateCosts();
  1126. }
  1127. };
  1128. if (multithreaded) {
  1129. graphUpdates.EnableMultithreading();
  1130. }
  1131. }
  1132. /// <summary>Does simple error checking</summary>
  1133. internal void VerifyIntegrity () {
  1134. if (active != this) {
  1135. throw new System.Exception("Singleton pattern broken. Make sure you only have one AstarPath object in the scene");
  1136. }
  1137. if (data == null) {
  1138. throw new System.NullReferenceException("data is null... A* not set up correctly?");
  1139. }
  1140. if (data.graphs == null) {
  1141. data.graphs = new NavGraph[0];
  1142. data.UpdateShortcuts();
  1143. }
  1144. }
  1145. /// <summary>\cond internal</summary>
  1146. /// <summary>
  1147. /// Internal method to make sure <see cref="active"/> is set to this object and that <see cref="data"/> is not null.
  1148. /// Also calls OnEnable for the <see cref="colorSettings"/> and initializes data.userConnections if it wasn't initialized before
  1149. ///
  1150. /// Warning: This is mostly for use internally by the system.
  1151. /// </summary>
  1152. public void ConfigureReferencesInternal () {
  1153. active = this;
  1154. data = data ?? new AstarData();
  1155. colorSettings = colorSettings ?? new AstarColor();
  1156. colorSettings.PushToStatic(this);
  1157. }
  1158. /// <summary>\endcond</summary>
  1159. /// <summary>Calls AstarProfiler.InitializeFastProfile</summary>
  1160. void InitializeProfiler () {
  1161. AstarProfiler.InitializeFastProfile(new string[14] {
  1162. "Prepare", //0
  1163. "Initialize", //1
  1164. "CalculateStep", //2
  1165. "Trace", //3
  1166. "Open", //4
  1167. "UpdateAllG", //5
  1168. "Add", //6
  1169. "Remove", //7
  1170. "PreProcessing", //8
  1171. "Callback", //9
  1172. "Overhead", //10
  1173. "Log", //11
  1174. "ReturnPaths", //12
  1175. "PostPathCallback" //13
  1176. });
  1177. }
  1178. /// <summary>
  1179. /// Initializes the AstarData class.
  1180. /// Searches for graph types, calls Awake on <see cref="data"/> and on all graphs
  1181. ///
  1182. /// See: AstarData.FindGraphTypes
  1183. /// </summary>
  1184. void InitializeAstarData () {
  1185. data.FindGraphTypes();
  1186. data.Awake();
  1187. data.UpdateShortcuts();
  1188. }
  1189. /// <summary>Cleans up meshes to avoid memory leaks</summary>
  1190. void OnDisable () {
  1191. gizmos.ClearCache();
  1192. }
  1193. /// <summary>
  1194. /// Clears up variables and other stuff, destroys graphs.
  1195. /// Note that when destroying an AstarPath object, all static variables such as callbacks will be cleared.
  1196. /// </summary>
  1197. void OnDestroy () {
  1198. // This class uses the [ExecuteInEditMode] attribute
  1199. // So OnDestroy is called even when not playing
  1200. // Don't do anything when not in play mode
  1201. if (!Application.isPlaying) return;
  1202. if (logPathResults == PathLog.Heavy)
  1203. Debug.Log("+++ AstarPath Component Destroyed - Cleaning Up Pathfinding Data +++");
  1204. if (active != this) return;
  1205. // Block until the pathfinding threads have
  1206. // completed their current path calculation
  1207. PausePathfinding();
  1208. navmeshUpdates.OnDisable();
  1209. euclideanEmbedding.dirty = false;
  1210. FlushWorkItems();
  1211. // Don't accept any more path calls to this AstarPath instance.
  1212. // This will cause all pathfinding threads to exit (if any exist)
  1213. pathProcessor.queue.TerminateReceivers();
  1214. if (logPathResults == PathLog.Heavy)
  1215. Debug.Log("Processing Possible Work Items");
  1216. // Stop the graph update thread (if it is running)
  1217. graphUpdates.DisableMultithreading();
  1218. // Try to join pathfinding threads
  1219. pathProcessor.JoinThreads();
  1220. if (logPathResults == PathLog.Heavy)
  1221. Debug.Log("Returning Paths");
  1222. // Return all paths
  1223. pathReturnQueue.ReturnPaths(false);
  1224. if (logPathResults == PathLog.Heavy)
  1225. Debug.Log("Destroying Graphs");
  1226. // Clean up graph data
  1227. // Data may be null if this object was never enabled because another A* instance existed.
  1228. if (data != null) data.OnDestroy();
  1229. if (logPathResults == PathLog.Heavy)
  1230. Debug.Log("Cleaning up variables");
  1231. // Clear variables up, static variables are good to clean up, otherwise the next scene might get weird data
  1232. // Clear all callbacks
  1233. OnAwakeSettings = null;
  1234. OnGraphPreScan = null;
  1235. OnGraphPostScan = null;
  1236. OnPathPreSearch = null;
  1237. OnPathPostSearch = null;
  1238. OnPreScan = null;
  1239. OnPostScan = null;
  1240. OnLatePostScan = null;
  1241. On65KOverflow = null;
  1242. OnGraphsUpdated = null;
  1243. active = null;
  1244. }
  1245. #region ScanMethods
  1246. /// <summary>
  1247. /// Floodfills starting from the specified node.
  1248. ///
  1249. /// Deprecated: Deprecated: Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes
  1250. /// </summary>
  1251. [System.Obsolete("Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes")]
  1252. public void FloodFill (GraphNode seed) {
  1253. }
  1254. /// <summary>
  1255. /// Floodfills starting from 'seed' using the specified area.
  1256. ///
  1257. /// Deprecated: Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes
  1258. /// </summary>
  1259. [System.Obsolete("Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes")]
  1260. public void FloodFill (GraphNode seed, uint area) {
  1261. }
  1262. /// <summary>
  1263. /// Floodfills all graphs and updates areas for every node.
  1264. /// The different colored areas that you see in the scene view when looking at graphs
  1265. /// are called just 'areas', this method calculates which nodes are in what areas.
  1266. /// See: Pathfinding.Node.area
  1267. ///
  1268. /// Deprecated: Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now.
  1269. /// </summary>
  1270. [ContextMenu("Flood Fill Graphs")]
  1271. [System.Obsolete("Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now.")]
  1272. public void FloodFill () {
  1273. hierarchicalGraph.RecalculateAll();
  1274. workItems.OnFloodFill();
  1275. }
  1276. /// <summary>
  1277. /// Returns a new global node index.
  1278. /// Warning: This method should not be called directly. It is used by the GraphNode constructor.
  1279. /// </summary>
  1280. internal int GetNewNodeIndex () {
  1281. return pathProcessor.GetNewNodeIndex();
  1282. }
  1283. /// <summary>
  1284. /// Initializes temporary path data for a node.
  1285. /// Warning: This method should not be called directly. It is used by the GraphNode constructor.
  1286. /// </summary>
  1287. internal void InitializeNode (GraphNode node) {
  1288. pathProcessor.InitializeNode(node);
  1289. }
  1290. /// <summary>
  1291. /// Internal method to destroy a given node.
  1292. /// This is to be called after the node has been disconnected from the graph so that it cannot be reached from any other nodes.
  1293. /// It should only be called during graph updates, that is when the pathfinding threads are either not running or paused.
  1294. ///
  1295. /// Warning: This method should not be called by user code. It is used internally by the system.
  1296. /// </summary>
  1297. internal void DestroyNode (GraphNode node) {
  1298. pathProcessor.DestroyNode(node);
  1299. }
  1300. /// <summary>
  1301. /// Blocks until all pathfinding threads are paused and blocked.
  1302. /// Deprecated: Use <see cref="PausePathfinding"/> instead. Make sure to call Release on the returned lock.
  1303. /// </summary>
  1304. [System.Obsolete("Use PausePathfinding instead. Make sure to call Release on the returned lock.", true)]
  1305. public void BlockUntilPathQueueBlocked () {
  1306. }
  1307. /// <summary>
  1308. /// Blocks until all pathfinding threads are paused and blocked.
  1309. ///
  1310. /// <code>
  1311. /// var graphLock = AstarPath.active.PausePathfinding();
  1312. /// // Here we can modify the graphs safely. For example by adding a new node to a point graph
  1313. /// var node = AstarPath.active.data.pointGraph.AddNode((Int3) new Vector3(3, 1, 4));
  1314. ///
  1315. /// // Allow pathfinding to resume
  1316. /// graphLock.Release();
  1317. /// </code>
  1318. ///
  1319. /// Returns: A lock object. You need to call <see cref="Pathfinding.PathProcessor.GraphUpdateLock.Release"/> on that object to allow pathfinding to resume.
  1320. /// Note: In most cases this should not be called from user code. Use the <see cref="AddWorkItem"/> method instead.
  1321. ///
  1322. /// See: <see cref="AddWorkItem"/>
  1323. /// </summary>
  1324. public PathProcessor.GraphUpdateLock PausePathfinding () {
  1325. return pathProcessor.PausePathfinding(true);
  1326. }
  1327. /// <summary>Blocks the path queue so that e.g work items can be performed</summary>
  1328. PathProcessor.GraphUpdateLock PausePathfindingSoon () {
  1329. return pathProcessor.PausePathfinding(false);
  1330. }
  1331. /// <summary>
  1332. /// Scans a particular graph.
  1333. /// Calling this method will recalculate the specified graph.
  1334. /// This method is pretty slow (depending on graph type and graph complexity of course), so it is advisable to use
  1335. /// smaller graph updates whenever possible.
  1336. ///
  1337. /// <code>
  1338. /// // Recalculate all graphs
  1339. /// AstarPath.active.Scan();
  1340. ///
  1341. /// // Recalculate only the first grid graph
  1342. /// var graphToScan = AstarPath.active.data.gridGraph;
  1343. /// AstarPath.active.Scan(graphToScan);
  1344. ///
  1345. /// // Recalculate only the first and third graphs
  1346. /// var graphsToScan = new [] { AstarPath.active.data.graphs[0], AstarPath.active.data.graphs[2] };
  1347. /// AstarPath.active.Scan(graphsToScan);
  1348. /// </code>
  1349. ///
  1350. /// See: graph-updates (view in online documentation for working links)
  1351. /// See: ScanAsync
  1352. /// </summary>
  1353. public void Scan (NavGraph graphToScan) {
  1354. if (graphToScan == null) throw new System.ArgumentNullException();
  1355. Scan(new NavGraph[] { graphToScan });
  1356. }
  1357. /// <summary>
  1358. /// Scans all specified graphs.
  1359. ///
  1360. /// Calling this method will recalculate all specified graphs or all graphs if the graphsToScan parameter is null.
  1361. /// This method is pretty slow (depending on graph type and graph complexity of course), so it is advisable to use
  1362. /// smaller graph updates whenever possible.
  1363. ///
  1364. /// <code>
  1365. /// // Recalculate all graphs
  1366. /// AstarPath.active.Scan();
  1367. ///
  1368. /// // Recalculate only the first grid graph
  1369. /// var graphToScan = AstarPath.active.data.gridGraph;
  1370. /// AstarPath.active.Scan(graphToScan);
  1371. ///
  1372. /// // Recalculate only the first and third graphs
  1373. /// var graphsToScan = new [] { AstarPath.active.data.graphs[0], AstarPath.active.data.graphs[2] };
  1374. /// AstarPath.active.Scan(graphsToScan);
  1375. /// </code>
  1376. ///
  1377. /// See: graph-updates (view in online documentation for working links)
  1378. /// See: ScanAsync
  1379. /// </summary>
  1380. /// <param name="graphsToScan">The graphs to scan. If this parameter is null then all graphs will be scanned</param>
  1381. public void Scan (NavGraph[] graphsToScan = null) {
  1382. var prevProgress = new Progress();
  1383. Profiler.BeginSample("Scan");
  1384. Profiler.BeginSample("Init");
  1385. foreach (var p in ScanAsync(graphsToScan)) {
  1386. if (prevProgress.description != p.description) {
  1387. #if !NETFX_CORE && UNITY_EDITOR
  1388. Profiler.EndSample();
  1389. Profiler.BeginSample(p.description);
  1390. // Log progress to the console
  1391. System.Console.WriteLine(p.description);
  1392. prevProgress = p;
  1393. #endif
  1394. }
  1395. }
  1396. Profiler.EndSample();
  1397. Profiler.EndSample();
  1398. }
  1399. /// <summary>
  1400. /// Scans a particular graph asynchronously. This is a IEnumerable, you can loop through it to get the progress
  1401. /// <code>
  1402. /// foreach (Progress progress in AstarPath.active.ScanAsync()) {
  1403. /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%");
  1404. /// }
  1405. /// </code>
  1406. /// You can scan graphs asyncronously by yielding when you loop through the progress.
  1407. /// Note that this does not guarantee a good framerate, but it will allow you
  1408. /// to at least show a progress bar during scanning.
  1409. /// <code>
  1410. /// IEnumerator Start () {
  1411. /// foreach (Progress progress in AstarPath.active.ScanAsync()) {
  1412. /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%");
  1413. /// yield return null;
  1414. /// }
  1415. /// }
  1416. /// </code>
  1417. ///
  1418. /// See: Scan
  1419. /// </summary>
  1420. public IEnumerable<Progress> ScanAsync (NavGraph graphToScan) {
  1421. if (graphToScan == null) throw new System.ArgumentNullException();
  1422. return ScanAsync(new NavGraph[] { graphToScan });
  1423. }
  1424. /// <summary>
  1425. /// Scans all specified graphs asynchronously. This is a IEnumerable, you can loop through it to get the progress
  1426. ///
  1427. /// <code>
  1428. /// foreach (Progress progress in AstarPath.active.ScanAsync()) {
  1429. /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%");
  1430. /// }
  1431. /// </code>
  1432. /// You can scan graphs asyncronously by yielding when you loop through the progress.
  1433. /// Note that this does not guarantee a good framerate, but it will allow you
  1434. /// to at least show a progress bar during scanning.
  1435. /// <code>
  1436. /// IEnumerator Start () {
  1437. /// foreach (Progress progress in AstarPath.active.ScanAsync()) {
  1438. /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%");
  1439. /// yield return null;
  1440. /// }
  1441. /// }
  1442. /// </code>
  1443. ///
  1444. /// See: Scan
  1445. /// </summary>
  1446. /// <param name="graphsToScan">The graphs to scan. If this parameter is null then all graphs will be scanned</param>
  1447. public IEnumerable<Progress> ScanAsync (NavGraph[] graphsToScan = null) {
  1448. if (graphsToScan == null) graphsToScan = graphs;
  1449. if (graphsToScan == null) {
  1450. yield break;
  1451. }
  1452. if (isScanning) throw new System.InvalidOperationException("Another async scan is already running");
  1453. isScanning = true;
  1454. VerifyIntegrity();
  1455. var graphUpdateLock = PausePathfinding();
  1456. // Make sure all paths that are in the queue to be returned
  1457. // are returned immediately
  1458. // Some modifiers (e.g the funnel modifier) rely on
  1459. // the nodes being valid when the path is returned
  1460. pathReturnQueue.ReturnPaths(false);
  1461. if (!Application.isPlaying) {
  1462. data.FindGraphTypes();
  1463. GraphModifier.FindAllModifiers();
  1464. }
  1465. yield return new Progress(0.05F, "Pre processing graphs");
  1466. if (OnPreScan != null) {
  1467. OnPreScan(this);
  1468. }
  1469. GraphModifier.TriggerEvent(GraphModifier.EventType.PreScan);
  1470. data.LockGraphStructure();
  1471. Physics2D.SyncTransforms();
  1472. var watch = System.Diagnostics.Stopwatch.StartNew();
  1473. Profiler.BeginSample("Destroy previous nodes");
  1474. // Destroy previous nodes
  1475. for (int i = 0; i < graphsToScan.Length; i++) {
  1476. if (graphsToScan[i] != null) {
  1477. ((IGraphInternals)graphsToScan[i]).DestroyAllNodes();
  1478. }
  1479. }
  1480. Profiler.EndSample();
  1481. // Loop through all graphs and scan them one by one
  1482. for (int i = 0; i < graphsToScan.Length; i++) {
  1483. // Skip null graphs
  1484. if (graphsToScan[i] == null) continue;
  1485. // Just used for progress information
  1486. // This graph will advance the progress bar from minp to maxp
  1487. float minp = Mathf.Lerp(0.1F, 0.8F, (float)(i)/(graphsToScan.Length));
  1488. float maxp = Mathf.Lerp(0.1F, 0.8F, (float)(i+0.95F)/(graphsToScan.Length));
  1489. var progressDescriptionPrefix = "Scanning graph " + (i+1) + " of " + graphsToScan.Length + " - ";
  1490. // Like a foreach loop but it gets a little complicated because of the exception
  1491. // handling (it is not possible to yield inside try-except clause).
  1492. var coroutine = ScanGraph(graphsToScan[i]).GetEnumerator();
  1493. while (true) {
  1494. try {
  1495. if (!coroutine.MoveNext()) break;
  1496. } catch {
  1497. isScanning = false;
  1498. data.UnlockGraphStructure();
  1499. graphUpdateLock.Release();
  1500. throw;
  1501. }
  1502. yield return coroutine.Current.MapTo(minp, maxp, progressDescriptionPrefix);
  1503. }
  1504. }
  1505. data.UnlockGraphStructure();
  1506. yield return new Progress(0.8F, "Post processing graphs");
  1507. if (OnPostScan != null) {
  1508. OnPostScan(this);
  1509. }
  1510. GraphModifier.TriggerEvent(GraphModifier.EventType.PostScan);
  1511. FlushWorkItems();
  1512. yield return new Progress(0.9F, "Computing areas");
  1513. hierarchicalGraph.RecalculateIfNecessary();
  1514. yield return new Progress(0.95F, "Late post processing");
  1515. // Signal that we have stopped scanning here
  1516. // Note that no yields can happen after this point
  1517. // since then other parts of the system can start to interfere
  1518. isScanning = false;
  1519. if (OnLatePostScan != null) {
  1520. OnLatePostScan(this);
  1521. }
  1522. GraphModifier.TriggerEvent(GraphModifier.EventType.LatePostScan);
  1523. euclideanEmbedding.dirty = true;
  1524. euclideanEmbedding.RecalculatePivots();
  1525. // Perform any blocking actions
  1526. FlushWorkItems();
  1527. // Resume pathfinding threads
  1528. graphUpdateLock.Release();
  1529. watch.Stop();
  1530. lastScanTime = (float)watch.Elapsed.TotalSeconds;
  1531. if (logPathResults != PathLog.None && logPathResults != PathLog.OnlyErrors) {
  1532. Debug.Log("Scanning - Process took "+(lastScanTime*1000).ToString("0")+" ms to complete");
  1533. }
  1534. }
  1535. IEnumerable<Progress> ScanGraph (NavGraph graph) {
  1536. if (OnGraphPreScan != null) {
  1537. yield return new Progress(0, "Pre processing");
  1538. OnGraphPreScan(graph);
  1539. }
  1540. yield return new Progress(0, "");
  1541. foreach (var p in ((IGraphInternals)graph).ScanInternal()) {
  1542. yield return p.MapTo(0, 0.95f);
  1543. }
  1544. yield return new Progress(0.95f, "Assigning graph indices");
  1545. Profiler.BeginSample("Assign graph indices");
  1546. // Assign the graph index to every node in the graph
  1547. graph.GetNodes(node => node.GraphIndex = (uint)graph.graphIndex);
  1548. Profiler.EndSample();
  1549. if (OnGraphPostScan != null) {
  1550. yield return new Progress(0.99f, "Post processing");
  1551. OnGraphPostScan(graph);
  1552. }
  1553. }
  1554. #endregion
  1555. private static int waitForPathDepth = 0;
  1556. /// <summary>
  1557. /// Wait for the specified path to be calculated.
  1558. /// Normally it takes a few frames for a path to get calculated and returned.
  1559. ///
  1560. /// Deprecated: This method has been renamed to <see cref="BlockUntilCalculated"/>.
  1561. /// </summary>
  1562. [System.Obsolete("This method has been renamed to BlockUntilCalculated")]
  1563. public static void WaitForPath (Path path) {
  1564. BlockUntilCalculated(path);
  1565. }
  1566. /// <summary>
  1567. /// Blocks until the path has been calculated.
  1568. ///
  1569. /// Normally it takes a few frames for a path to be calculated and returned.
  1570. /// This function will ensure that the path will be calculated when this function returns
  1571. /// and that the callback for that path has been called.
  1572. ///
  1573. /// If requesting a lot of paths in one go and waiting for the last one to complete,
  1574. /// it will calculate most of the paths in the queue (only most if using multithreading, all if not using multithreading).
  1575. ///
  1576. /// Use this function only if you really need to.
  1577. /// There is a point to spreading path calculations out over several frames.
  1578. /// It smoothes out the framerate and makes sure requesting a large
  1579. /// number of paths at the same time does not cause lag.
  1580. ///
  1581. /// Note: Graph updates and other callbacks might get called during the execution of this function.
  1582. ///
  1583. /// When the pathfinder is shutting down. I.e in OnDestroy, this function will not do anything.
  1584. ///
  1585. /// Throws: Exception if pathfinding is not initialized properly for this scene (most likely no AstarPath object exists)
  1586. /// or if the path has not been started yet.
  1587. /// Also throws an exception if critical errors occur such as when the pathfinding threads have crashed (which should not happen in normal cases).
  1588. /// This prevents an infinite loop while waiting for the path.
  1589. ///
  1590. /// See: Pathfinding.Path.WaitForPath
  1591. /// See: Pathfinding.Path.BlockUntilCalculated
  1592. /// </summary>
  1593. /// <param name="path">The path to wait for. The path must be started, otherwise an exception will be thrown.</param>
  1594. public static void BlockUntilCalculated (Path path) {
  1595. if (active == null)
  1596. throw new System.Exception("Pathfinding is not correctly initialized in this scene (yet?). " +
  1597. "AstarPath.active is null.\nDo not call this function in Awake");
  1598. if (path == null) throw new System.ArgumentNullException("Path must not be null");
  1599. if (active.pathProcessor.queue.IsTerminating) return;
  1600. if (path.PipelineState == PathState.Created) {
  1601. throw new System.Exception("The specified path has not been started yet.");
  1602. }
  1603. waitForPathDepth++;
  1604. if (waitForPathDepth == 5) {
  1605. Debug.LogError("You are calling the BlockUntilCalculated function recursively (maybe from a path callback). Please don't do this.");
  1606. }
  1607. if (path.PipelineState < PathState.ReturnQueue) {
  1608. if (active.IsUsingMultithreading) {
  1609. while (path.PipelineState < PathState.ReturnQueue) {
  1610. if (active.pathProcessor.queue.IsTerminating) {
  1611. waitForPathDepth--;
  1612. throw new System.Exception("Pathfinding Threads seem to have crashed.");
  1613. }
  1614. // Wait for threads to calculate paths
  1615. Thread.Sleep(1);
  1616. active.PerformBlockingActions(true);
  1617. }
  1618. } else {
  1619. while (path.PipelineState < PathState.ReturnQueue) {
  1620. if (active.pathProcessor.queue.IsEmpty && path.PipelineState != PathState.Processing) {
  1621. waitForPathDepth--;
  1622. throw new System.Exception("Critical error. Path Queue is empty but the path state is '" + path.PipelineState + "'");
  1623. }
  1624. // Calculate some paths
  1625. active.pathProcessor.TickNonMultithreaded();
  1626. active.PerformBlockingActions(true);
  1627. }
  1628. }
  1629. }
  1630. active.pathReturnQueue.ReturnPaths(false);
  1631. waitForPathDepth--;
  1632. }
  1633. /// <summary>
  1634. /// Will send a callback when it is safe to update nodes. This is defined as between the path searches.
  1635. /// This callback will only be sent once and is nulled directly after the callback has been sent.
  1636. /// When using more threads than one, calling this often might decrease pathfinding performance due to a lot of idling in the threads.
  1637. /// Not performance as in it will use much CPU power,
  1638. /// but performance as in the number of paths per second will probably go down (though your framerate might actually increase a tiny bit)
  1639. ///
  1640. /// You should only call this function from the main unity thread (i.e normal game code).
  1641. ///
  1642. /// Version: Since version 4.0 this is equivalent to AddWorkItem(new AstarWorkItem(callback)). Previously the
  1643. /// callbacks added using this method would not be ordered with respect to other work items, so they could be
  1644. /// executed before other work items or after them.
  1645. ///
  1646. /// Deprecated: Use <see cref="AddWorkItem(System.Action)"/> instead. Note the slight change in behavior (mentioned above).
  1647. /// </summary>
  1648. [System.Obsolete("Use AddWorkItem(System.Action) instead. Note the slight change in behavior (mentioned in the documentation).")]
  1649. public static void RegisterSafeUpdate (System.Action callback) {
  1650. active.AddWorkItem(new AstarWorkItem(callback));
  1651. }
  1652. /// <summary>
  1653. /// Adds the path to a queue so that it will be calculated as soon as possible.
  1654. /// The callback specified when constructing the path will be called when the path has been calculated.
  1655. /// Usually you should use the Seeker component instead of calling this function directly.
  1656. /// </summary>
  1657. /// <param name="path">The path that should be enqueued.</param>
  1658. /// <param name="pushToFront">If true, the path will be pushed to the front of the queue, bypassing all waiting paths and making it the next path to be calculated.
  1659. /// This can be useful if you have a path which you want to prioritize over all others. Be careful to not overuse it though.
  1660. /// If too many paths are put in the front of the queue often, this can lead to normal paths having to wait a very long time before being calculated.</param>
  1661. public static void StartPath (Path path, bool pushToFront = false) {
  1662. // Copy to local variable to avoid multithreading issues
  1663. var astar = active;
  1664. if (System.Object.ReferenceEquals(astar, null)) {
  1665. Debug.LogError("There is no AstarPath object in the scene or it has not been initialized yet");
  1666. return;
  1667. }
  1668. if (path.PipelineState != PathState.Created) {
  1669. throw new System.Exception("The path has an invalid state. Expected " + PathState.Created + " found " + path.PipelineState + "\n" +
  1670. "Make sure you are not requesting the same path twice");
  1671. }
  1672. if (astar.pathProcessor.queue.IsTerminating) {
  1673. path.FailWithError("No new paths are accepted");
  1674. return;
  1675. }
  1676. if (astar.graphs == null || astar.graphs.Length == 0) {
  1677. Debug.LogError("There are no graphs in the scene");
  1678. path.FailWithError("There are no graphs in the scene");
  1679. Debug.LogError(path.errorLog);
  1680. return;
  1681. }
  1682. path.Claim(astar);
  1683. // Will increment p.state to PathState.PathQueue
  1684. ((IPathInternals)path).AdvanceState(PathState.PathQueue);
  1685. if (pushToFront) {
  1686. astar.pathProcessor.queue.PushFront(path);
  1687. } else {
  1688. astar.pathProcessor.queue.Push(path);
  1689. }
  1690. // Outside of play mode, all path requests are synchronous
  1691. if (!Application.isPlaying) {
  1692. BlockUntilCalculated(path);
  1693. }
  1694. }
  1695. /// <summary>
  1696. /// Cached NNConstraint.None to avoid unnecessary allocations.
  1697. /// This should ideally be fixed by making NNConstraint an immutable class/struct.
  1698. /// </summary>
  1699. static readonly NNConstraint NNConstraintNone = NNConstraint.None;
  1700. /// <summary>
  1701. /// Returns the nearest node to a position.
  1702. /// This method will search through all graphs and query them for the closest node to this position, and then it will return the closest one of those.
  1703. ///
  1704. /// Equivalent to GetNearest(position, NNConstraint.None).
  1705. ///
  1706. /// <code>
  1707. /// // Find the closest node to this GameObject's position
  1708. /// GraphNode node = AstarPath.active.GetNearest(transform.position).node;
  1709. ///
  1710. /// if (node.Walkable) {
  1711. /// // Yay, the node is walkable, we can place a tower here or something
  1712. /// }
  1713. /// </code>
  1714. ///
  1715. /// See: Pathfinding.NNConstraint
  1716. /// </summary>
  1717. public NNInfo GetNearest (Vector3 position) {
  1718. return GetNearest(position, NNConstraintNone);
  1719. }
  1720. /// <summary>
  1721. /// Returns the nearest node to a position using the specified NNConstraint.
  1722. /// Searches through all graphs for their nearest nodes to the specified position and picks the closest one.
  1723. /// The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes.
  1724. ///
  1725. /// <code>
  1726. /// GraphNode node = AstarPath.active.GetNearest(transform.position, NNConstraint.Default).node;
  1727. /// </code>
  1728. ///
  1729. /// <code>
  1730. /// var constraint = NNConstraint.None;
  1731. ///
  1732. /// // Constrain the search to walkable nodes only
  1733. /// constraint.constrainWalkability = true;
  1734. /// constraint.walkable = true;
  1735. ///
  1736. /// // Constrain the search to only nodes with tag 3 or tag 5
  1737. /// // The 'tags' field is a bitmask
  1738. /// constraint.constrainTags = true;
  1739. /// constraint.tags = (1 << 3) | (1 << 5);
  1740. ///
  1741. /// var info = AstarPath.active.GetNearest(transform.position, constraint);
  1742. /// var node = info.node;
  1743. /// var closestPoint = info.position;
  1744. /// </code>
  1745. ///
  1746. /// See: Pathfinding.NNConstraint
  1747. /// </summary>
  1748. public NNInfo GetNearest (Vector3 position, NNConstraint constraint) {
  1749. return GetNearest(position, constraint, null);
  1750. }
  1751. /// <summary>
  1752. /// Returns the nearest node to a position using the specified NNConstraint.
  1753. /// Searches through all graphs for their nearest nodes to the specified position and picks the closest one.
  1754. /// The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes.
  1755. /// See: Pathfinding.NNConstraint
  1756. /// </summary>
  1757. public NNInfo GetNearest (Vector3 position, NNConstraint constraint, GraphNode hint) {
  1758. // Cache property lookup
  1759. var graphs = this.graphs;
  1760. float minDist = float.PositiveInfinity;
  1761. NNInfoInternal nearestNode = new NNInfoInternal();
  1762. int nearestGraph = -1;
  1763. if (graphs != null) {
  1764. for (int i = 0; i < graphs.Length; i++) {
  1765. NavGraph graph = graphs[i];
  1766. // Check if this graph should be searched
  1767. if (graph == null || !constraint.SuitableGraph(i, graph)) {
  1768. continue;
  1769. }
  1770. NNInfoInternal nnInfo;
  1771. if (fullGetNearestSearch) {
  1772. // Slower nearest node search
  1773. // this will try to find a node which is suitable according to the constraint
  1774. nnInfo = graph.GetNearestForce(position, constraint);
  1775. } else {
  1776. // Fast nearest node search
  1777. // just find a node close to the position without using the constraint that much
  1778. // (unless that comes essentially 'for free')
  1779. nnInfo = graph.GetNearest(position, constraint);
  1780. }
  1781. GraphNode node = nnInfo.node;
  1782. // No node found in this graph
  1783. if (node == null) {
  1784. continue;
  1785. }
  1786. // Distance to the closest point on the node from the requested position
  1787. float dist = ((Vector3)nnInfo.clampedPosition-position).magnitude;
  1788. #pragma warning disable 0618
  1789. if (prioritizeGraphs && dist < prioritizeGraphsLimit) {
  1790. #pragma warning restore 0618
  1791. // The node is close enough, choose this graph and discard all others
  1792. minDist = dist;
  1793. nearestNode = nnInfo;
  1794. nearestGraph = i;
  1795. break;
  1796. } else {
  1797. // Choose the best node found so far
  1798. if (dist < minDist) {
  1799. minDist = dist;
  1800. nearestNode = nnInfo;
  1801. nearestGraph = i;
  1802. }
  1803. }
  1804. }
  1805. }
  1806. // No matches found
  1807. if (nearestGraph == -1) {
  1808. return new NNInfo();
  1809. }
  1810. // Check if a constrained node has already been set
  1811. if (nearestNode.constrainedNode != null) {
  1812. nearestNode.node = nearestNode.constrainedNode;
  1813. nearestNode.clampedPosition = nearestNode.constClampedPosition;
  1814. }
  1815. if (!fullGetNearestSearch && nearestNode.node != null && !constraint.Suitable(nearestNode.node)) {
  1816. // Otherwise, perform a check to force the graphs to check for a suitable node
  1817. NNInfoInternal nnInfo = graphs[nearestGraph].GetNearestForce(position, constraint);
  1818. if (nnInfo.node != null) {
  1819. nearestNode = nnInfo;
  1820. }
  1821. }
  1822. if (!constraint.Suitable(nearestNode.node) || (constraint.constrainDistance && (nearestNode.clampedPosition - position).sqrMagnitude > maxNearestNodeDistanceSqr)) {
  1823. return new NNInfo();
  1824. }
  1825. // Convert to NNInfo which doesn't have all the internal fields
  1826. return new NNInfo(nearestNode);
  1827. }
  1828. /// <summary>
  1829. /// Returns the node closest to the ray (slow).
  1830. /// Warning: This function is brute-force and very slow, use with caution
  1831. /// </summary>
  1832. public GraphNode GetNearest (Ray ray) {
  1833. if (graphs == null) return null;
  1834. float minDist = Mathf.Infinity;
  1835. GraphNode nearestNode = null;
  1836. Vector3 lineDirection = ray.direction;
  1837. Vector3 lineOrigin = ray.origin;
  1838. for (int i = 0; i < graphs.Length; i++) {
  1839. NavGraph graph = graphs[i];
  1840. graph.GetNodes(node => {
  1841. Vector3 pos = (Vector3)node.position;
  1842. Vector3 p = lineOrigin+(Vector3.Dot(pos-lineOrigin, lineDirection)*lineDirection);
  1843. float tmp = Mathf.Abs(p.x-pos.x);
  1844. tmp *= tmp;
  1845. if (tmp > minDist) return;
  1846. tmp = Mathf.Abs(p.z-pos.z);
  1847. tmp *= tmp;
  1848. if (tmp > minDist) return;
  1849. float dist = (p-pos).sqrMagnitude;
  1850. if (dist < minDist) {
  1851. minDist = dist;
  1852. nearestNode = node;
  1853. }
  1854. return;
  1855. });
  1856. }
  1857. return nearestNode;
  1858. }
  1859. }