LayerGridGraphGenerator.cs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266
  1. #if !ASTAR_NO_GRID_GRAPH
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. using Pathfinding.Serialization;
  5. namespace Pathfinding {
  6. /// <summary>
  7. /// Grid Graph, supports layered worlds.
  8. /// [Open online documentation to see images]
  9. /// The GridGraph is great in many ways, reliable, easily configured and updatable during runtime.
  10. /// But it lacks support for worlds which have multiple layers, such as a building with multiple floors.
  11. /// That's where this graph type comes in. It supports basically the same stuff as the grid graph, but also multiple layers.
  12. /// It uses a bit more memory than a regular grid graph, but is otherwise equivalent.
  13. ///
  14. /// [Open online documentation to see images]
  15. ///
  16. /// See: <see cref="GridGraph"/>
  17. /// </summary>
  18. [Pathfinding.Util.Preserve]
  19. public class LayerGridGraph : GridGraph, IUpdatableGraph {
  20. // This function will be called when this graph is destroyed
  21. protected override void OnDestroy () {
  22. base.OnDestroy();
  23. // Clean up a reference in a static variable which otherwise should point to this graph forever and stop the GC from collecting it
  24. RemoveGridGraphFromStatic();
  25. }
  26. void RemoveGridGraphFromStatic () {
  27. LevelGridNode.SetGridGraph(active.data.GetGraphIndex(this), null);
  28. }
  29. /// <summary>
  30. /// Number of layers.
  31. /// Warning: Do not modify this variable
  32. /// </summary>
  33. [JsonMember]
  34. internal int layerCount;
  35. /// <summary>If two layered nodes are too close, they will be merged</summary>
  36. [JsonMember]
  37. public float mergeSpanRange = 0.5F;
  38. /// <summary>Nodes with a short distance to the node above it will be set unwalkable</summary>
  39. [JsonMember]
  40. public float characterHeight = 0.4F;
  41. internal int lastScannedWidth;
  42. internal int lastScannedDepth;
  43. public override bool uniformWidthDepthGrid {
  44. get {
  45. return false;
  46. }
  47. }
  48. public override int LayerCount {
  49. get {
  50. return layerCount;
  51. }
  52. }
  53. public override int CountNodes () {
  54. if (nodes == null) return 0;
  55. int counter = 0;
  56. for (int i = 0; i < nodes.Length; i++) {
  57. if (nodes[i] != null) counter++;
  58. }
  59. return counter;
  60. }
  61. public override void GetNodes (System.Action<GraphNode> action) {
  62. if (nodes == null) return;
  63. for (int i = 0; i < nodes.Length; i++) {
  64. if (nodes[i] != null) action(nodes[i]);
  65. }
  66. }
  67. protected override List<GraphNode> GetNodesInRegion (Bounds b, GraphUpdateShape shape) {
  68. var rect = GetRectFromBounds(b);
  69. if (nodes == null || !rect.IsValid() || nodes.Length != width*depth*layerCount) {
  70. return Pathfinding.Util.ListPool<GraphNode>.Claim();
  71. }
  72. // Get a buffer we can use
  73. var inArea = Pathfinding.Util.ListPool<GraphNode>.Claim(rect.Width*rect.Height*layerCount);
  74. // Loop through all nodes in the rectangle
  75. for (int l = 0; l < layerCount; l++) {
  76. var lwd = l * width * depth;
  77. for (int x = rect.xmin; x <= rect.xmax; x++) {
  78. for (int z = rect.ymin; z <= rect.ymax; z++) {
  79. int index = lwd + z*width + x;
  80. GraphNode node = nodes[index];
  81. // If it is contained in the bounds (and optionally the shape)
  82. // then add it to the buffer
  83. if (node != null && b.Contains((Vector3)node.position) && (shape == null || shape.Contains((Vector3)node.position))) {
  84. inArea.Add(node);
  85. }
  86. }
  87. }
  88. }
  89. return inArea;
  90. }
  91. public override List<GraphNode> GetNodesInRegion (IntRect rect) {
  92. // Get a buffer we can use
  93. var inArea = Pathfinding.Util.ListPool<GraphNode>.Claim();
  94. // Rect which covers the whole grid
  95. var gridRect = new IntRect(0, 0, width-1, depth-1);
  96. // Clamp the rect to the grid
  97. rect = IntRect.Intersection(rect, gridRect);
  98. if (nodes == null || !rect.IsValid() || nodes.Length != width*depth*layerCount) return inArea;
  99. for (int l = 0; l < layerCount; l++) {
  100. var lwd = l * Width * Depth;
  101. for (int z = rect.ymin; z <= rect.ymax; z++) {
  102. var offset = lwd + z*Width;
  103. for (int x = rect.xmin; x <= rect.xmax; x++) {
  104. var node = nodes[offset + x];
  105. if (node != null) {
  106. inArea.Add(node);
  107. }
  108. }
  109. }
  110. }
  111. return inArea;
  112. }
  113. /// <summary>
  114. /// Get all nodes in a rectangle.
  115. /// Returns: The number of nodes written to the buffer.
  116. /// </summary>
  117. /// <param name="rect">Region in which to return nodes. It will be clamped to the grid.</param>
  118. /// <param name="buffer">Buffer in which the nodes will be stored. Should be at least as large as the number of nodes that can exist in that region.</param>
  119. public override int GetNodesInRegion (IntRect rect, GridNodeBase[] buffer) {
  120. // Clamp the rect to the grid
  121. // Rect which covers the whole grid
  122. var gridRect = new IntRect(0, 0, width-1, depth-1);
  123. rect = IntRect.Intersection(rect, gridRect);
  124. if (nodes == null || !rect.IsValid() || nodes.Length != width*depth*layerCount) return 0;
  125. int counter = 0;
  126. try {
  127. for (int l = 0; l < layerCount; l++) {
  128. var lwd = l * Width * Depth;
  129. for (int z = rect.ymin; z <= rect.ymax; z++) {
  130. var offset = lwd + z*Width;
  131. for (int x = rect.xmin; x <= rect.xmax; x++) {
  132. var node = nodes[offset + x];
  133. if (node != null) {
  134. buffer[counter] = node;
  135. counter++;
  136. }
  137. }
  138. }
  139. }
  140. } catch (System.IndexOutOfRangeException) {
  141. // Catch the exception which 'buffer[counter] = node' would throw if the buffer was too small
  142. throw new System.ArgumentException("Buffer is too small");
  143. }
  144. return counter;
  145. }
  146. /// <summary>
  147. /// Node in the specified cell in the first layer.
  148. /// Returns null if the coordinate is outside the grid.
  149. ///
  150. /// <code>
  151. /// var gg = AstarPath.active.data.gridGraph;
  152. /// int x = 5;
  153. /// int z = 8;
  154. /// GridNodeBase node = gg.GetNode(x, z);
  155. /// </code>
  156. ///
  157. /// If you know the coordinate is inside the grid and you are looking to maximize performance then you
  158. /// can look up the node in the internal array directly which is slightly faster.
  159. /// See: <see cref="nodes"/>
  160. /// </summary>
  161. public override GridNodeBase GetNode (int x, int z) {
  162. if (x < 0 || z < 0 || x >= width || z >= depth) return null;
  163. return nodes[x + z*width];
  164. }
  165. /// <summary>
  166. /// Node in the specified cell.
  167. /// Returns null if the coordinate is outside the grid.
  168. ///
  169. /// If you know the coordinate is inside the grid and you are looking to maximize performance then you
  170. /// can look up the node in the internal array directly which is slightly faster.
  171. /// See: <see cref="nodes"/>
  172. /// </summary>
  173. public GridNodeBase GetNode (int x, int z, int layer) {
  174. if (x < 0 || z < 0 || x >= width || z >= depth || layer < 0 || layer >= layerCount) return null;
  175. return nodes[x + z*width + layer*width*depth];
  176. }
  177. void IUpdatableGraph.UpdateArea (GraphUpdateObject o) {
  178. if (nodes == null || nodes.Length != width*depth*layerCount) {
  179. Debug.LogWarning("The Grid Graph is not scanned, cannot update area ");
  180. //Not scanned
  181. return;
  182. }
  183. IntRect originalRect, affectRect, physicsRect;
  184. bool willChangeWalkability;
  185. int erosion;
  186. CalculateAffectedRegions(o, out originalRect, out affectRect, out physicsRect, out willChangeWalkability, out erosion);
  187. bool willChangeNodeInstances = (o is LayerGridGraphUpdate && ((LayerGridGraphUpdate)o).recalculateNodes);
  188. bool preserveExistingNodes = (o is LayerGridGraphUpdate ? ((LayerGridGraphUpdate)o).preserveExistingNodes : !o.resetPenaltyOnPhysics);
  189. if (o.trackChangedNodes && willChangeNodeInstances) {
  190. Debug.LogError("Cannot track changed nodes when creating or deleting nodes.\nWill not update LayerGridGraph");
  191. return;
  192. }
  193. // Rect which covers the whole grid
  194. var gridRect = new IntRect(0, 0, width-1, depth-1);
  195. IntRect clampedRect = IntRect.Intersection(affectRect, gridRect);
  196. // Mark nodes that might be changed
  197. if (!willChangeNodeInstances) {
  198. for (int x = clampedRect.xmin; x <= clampedRect.xmax; x++) {
  199. for (int z = clampedRect.ymin; z <= clampedRect.ymax; z++) {
  200. for (int y = 0; y < layerCount; y++) {
  201. o.WillUpdateNode(nodes[y*width*depth + z*width+x]);
  202. }
  203. }
  204. }
  205. }
  206. // Update Physics
  207. if (o.updatePhysics && !o.modifyWalkability) {
  208. collision.Initialize(transform, nodeSize);
  209. clampedRect = IntRect.Intersection(physicsRect, gridRect);
  210. for (int x = clampedRect.xmin; x <= clampedRect.xmax; x++) {
  211. for (int z = clampedRect.ymin; z <= clampedRect.ymax; z++) {
  212. RecalculateCell(x, z, !preserveExistingNodes, false);
  213. }
  214. }
  215. for (int x = clampedRect.xmin; x <= clampedRect.xmax; x++) {
  216. for (int z = clampedRect.ymin; z <= clampedRect.ymax; z++) {
  217. CalculateConnections(x, z);
  218. }
  219. }
  220. }
  221. // Apply GUO
  222. clampedRect = IntRect.Intersection(originalRect, gridRect);
  223. for (int x = clampedRect.xmin; x <= clampedRect.xmax; x++) {
  224. for (int z = clampedRect.ymin; z <= clampedRect.ymax; z++) {
  225. for (int y = 0; y < layerCount; y++) {
  226. int index = y*width*depth + z*width+x;
  227. var node = nodes[index];
  228. if (node == null) continue;
  229. if (willChangeWalkability) {
  230. node.Walkable = node.WalkableErosion;
  231. if (o.bounds.Contains((Vector3)node.position)) o.Apply(node);
  232. node.WalkableErosion = node.Walkable;
  233. } else {
  234. if (o.bounds.Contains((Vector3)node.position)) o.Apply(node);
  235. }
  236. }
  237. }
  238. }
  239. // Recalculate connections
  240. if (willChangeWalkability && erosion == 0) {
  241. clampedRect = IntRect.Intersection(affectRect, gridRect);
  242. for (int x = clampedRect.xmin; x <= clampedRect.xmax; x++) {
  243. for (int z = clampedRect.ymin; z <= clampedRect.ymax; z++) {
  244. CalculateConnections(x, z);
  245. }
  246. }
  247. } else if (willChangeWalkability && erosion > 0) {
  248. clampedRect = IntRect.Union(originalRect, physicsRect);
  249. IntRect erosionRect1 = clampedRect.Expand(erosion);
  250. IntRect erosionRect2 = erosionRect1.Expand(erosion);
  251. erosionRect1 = IntRect.Intersection(erosionRect1, gridRect);
  252. erosionRect2 = IntRect.Intersection(erosionRect2, gridRect);
  253. /*
  254. * all nodes inside clampedRect might have had their walkability changed
  255. * all nodes inside erosionRect1 might get affected by erosion from clampedRect and erosionRect2
  256. * all nodes inside erosionRect2 (but outside erosionRect1) will be reset to previous walkability
  257. * after calculation since their erosion might not be correctly calculated (nodes outside erosionRect2 would maybe have effect)
  258. */
  259. for (int x = erosionRect2.xmin; x <= erosionRect2.xmax; x++) {
  260. for (int z = erosionRect2.ymin; z <= erosionRect2.ymax; z++) {
  261. for (int y = 0; y < layerCount; y++) {
  262. int index = y*width*depth + z*width+x;
  263. var node = nodes[index];
  264. if (node == null) continue;
  265. bool tmp = node.Walkable;
  266. node.Walkable = node.WalkableErosion;
  267. if (!erosionRect1.Contains(x, z)) {
  268. //Save the border's walkabilty data in bit 16 (will be reset later)
  269. node.TmpWalkable = tmp;
  270. }
  271. }
  272. }
  273. }
  274. for (int x = erosionRect2.xmin; x <= erosionRect2.xmax; x++) {
  275. for (int z = erosionRect2.ymin; z <= erosionRect2.ymax; z++) {
  276. CalculateConnections(x, z);
  277. }
  278. }
  279. // Erode the walkable area
  280. ErodeWalkableArea(erosionRect2.xmin, erosionRect2.ymin, erosionRect2.xmax+1, erosionRect2.ymax+1);
  281. for (int x = erosionRect2.xmin; x <= erosionRect2.xmax; x++) {
  282. for (int z = erosionRect2.ymin; z <= erosionRect2.ymax; z++) {
  283. if (erosionRect1.Contains(x, z)) continue;
  284. for (int y = 0; y < layerCount; y++) {
  285. int index = y*width*depth + z*width+x;
  286. var node = nodes[index];
  287. if (node == null) continue;
  288. // Restore temporarily stored data
  289. node.Walkable = node.TmpWalkable;
  290. }
  291. }
  292. }
  293. // Recalculate connections of all affected nodes
  294. for (int x = erosionRect2.xmin; x <= erosionRect2.xmax; x++) {
  295. for (int z = erosionRect2.ymin; z <= erosionRect2.ymax; z++) {
  296. CalculateConnections(x, z);
  297. }
  298. }
  299. }
  300. }
  301. protected override IEnumerable<Progress> ScanInternal () {
  302. // Not possible to have a negative node size
  303. if (nodeSize <= 0) yield break;
  304. UpdateTransform();
  305. // This is just an artificial limit. Graphs larger than this use quite a lot of memory.
  306. if (width > 1024 || depth > 1024) {
  307. Debug.LogError("One of the grid's sides is longer than 1024 nodes");
  308. yield break;
  309. }
  310. lastScannedWidth = width;
  311. lastScannedDepth = depth;
  312. SetUpOffsetsAndCosts();
  313. LevelGridNode.SetGridGraph((int)graphIndex, this);
  314. // This is also enforced in the inspector, but just in case it was set from a script we enforce it here as well
  315. maxClimb = Mathf.Clamp(maxClimb, 0, characterHeight);
  316. collision = collision ?? new GraphCollision();
  317. collision.Initialize(transform, nodeSize);
  318. int progressCounter = 0;
  319. const int YieldEveryNNodes = 1000;
  320. // Create an array to hold all nodes (if there is more than one layer, this array will be expanded)
  321. layerCount = 1;
  322. nodes = new LevelGridNode[width*depth*layerCount];
  323. for (int z = 0; z < depth; z++) {
  324. // Yield with a progress value at most every N nodes
  325. if (progressCounter >= YieldEveryNNodes) {
  326. progressCounter = 0;
  327. yield return new Progress(Mathf.Lerp(0.0f, 0.8f, z/(float)depth), "Creating nodes");
  328. }
  329. progressCounter += width;
  330. for (int x = 0; x < width; x++) {
  331. RecalculateCell(x, z);
  332. }
  333. }
  334. for (int z = 0; z < depth; z++) {
  335. // Yield with a progress value at most every N nodes
  336. if (progressCounter >= YieldEveryNNodes) {
  337. progressCounter = 0;
  338. yield return new Progress(Mathf.Lerp(0.8f, 0.9f, z/(float)depth), "Calculating connections");
  339. }
  340. progressCounter += width;
  341. for (int x = 0; x < width; x++) {
  342. CalculateConnections(x, z);
  343. }
  344. }
  345. yield return new Progress(0.95f, "Calculating Erosion");
  346. for (int i = 0; i < nodes.Length; i++) {
  347. var node = nodes[i] as LevelGridNode;
  348. if (node == null) continue;
  349. // Set the node to be unwalkable if it hasn't got any connections
  350. if (!node.HasAnyGridConnections()) {
  351. node.Walkable = false;
  352. node.WalkableErosion = node.Walkable;
  353. }
  354. }
  355. ErodeWalkableArea();
  356. }
  357. /// <summary>Struct returned by <see cref="SampleHeights"/></summary>
  358. protected struct HeightSample {
  359. public Vector3 position;
  360. public RaycastHit hit;
  361. public float height;
  362. public bool walkable;
  363. }
  364. /// <summary>Sorts RaycastHits by distance</summary>
  365. class HitComparer : IComparer<RaycastHit> {
  366. public int Compare (RaycastHit a, RaycastHit b) {
  367. return a.distance.CompareTo(b.distance);
  368. }
  369. }
  370. /// <summary>Sorts RaycastHits by distance</summary>
  371. static readonly HitComparer comparer = new HitComparer();
  372. /// <summary>Internal buffer used by <see cref="SampleHeights"/></summary>
  373. static HeightSample[] heightSampleBuffer = new HeightSample[4];
  374. /// <summary>
  375. /// Fires a ray from the sky and returns a sample for everything it hits.
  376. /// The samples are ordered from the ground up.
  377. /// Samples that are close together are merged (see <see cref="Pathfinding.LayerGridGraph.mergeSpanRange)"/>.
  378. ///
  379. /// Warning: The returned array is ephermal. It will be invalidated when this method is called again.
  380. /// If you need persistent results you should copy it.
  381. ///
  382. /// The returned array may be larger than the actual number of hits, the numHits out parameter indicates how many hits there actually were.
  383. ///
  384. /// See: GraphCollision.
  385. /// </summary>
  386. protected static HeightSample[] SampleHeights (GraphCollision collision, float mergeSpanRange, Vector3 position, out int numHits) {
  387. int raycastHits;
  388. var hits = collision.CheckHeightAll(position, out raycastHits);
  389. // Sort by distance in increasing order (so hits are ordered from highest y coordinate to lowest)
  390. System.Array.Sort(hits, 0, raycastHits, comparer);
  391. if (raycastHits > heightSampleBuffer.Length) heightSampleBuffer = new HeightSample[Mathf.Max(heightSampleBuffer.Length*2, raycastHits)];
  392. var buffer = heightSampleBuffer;
  393. if (raycastHits == 0) {
  394. buffer[0] = new HeightSample {
  395. position = position,
  396. height = float.PositiveInfinity,
  397. walkable = !collision.unwalkableWhenNoGround && collision.Check(position),
  398. };
  399. numHits = 1;
  400. return buffer;
  401. } else {
  402. int dstIndex = 0;
  403. for (int i = raycastHits - 1; i >= 0; i--) {
  404. // Merge together collider hits which are very close to each other
  405. if (i > 0 && hits[i].distance - hits[i-1].distance <= mergeSpanRange) i--;
  406. buffer[dstIndex] = new HeightSample {
  407. position = hits[i].point,
  408. hit = hits[i],
  409. walkable = collision.Check(hits[i].point),
  410. height = i > 0 ? hits[i].distance - hits[i-1].distance : float.PositiveInfinity,
  411. };
  412. dstIndex++;
  413. }
  414. numHits = dstIndex;
  415. return buffer;
  416. }
  417. }
  418. /// <summary>
  419. /// Recalculates single cell.
  420. ///
  421. /// For a layered grid graph this will recalculate all nodes at a specific (x,z) coordinate in the grid.
  422. /// For grid graphs this will simply recalculate the single node at those coordinates.
  423. ///
  424. /// Note: This must only be called when it is safe to update nodes.
  425. /// For example when scanning the graph or during a graph update.
  426. ///
  427. /// Note: This will not recalculate any connections as this method is often run for several adjacent nodes at a time.
  428. /// After you have recalculated all the nodes you will have to recalculate the connections for the changed nodes
  429. /// as well as their neighbours.
  430. /// See: CalculateConnections
  431. /// </summary>
  432. /// <param name="x">X coordinate of the cell</param>
  433. /// <param name="z">Z coordinate of the cell</param>
  434. /// <param name="resetPenalties">If true, the penalty of the nodes will be reset to the initial value as if the graph had just been scanned.</param>
  435. /// <param name="resetTags">If true, the penalty will be reset to zero (the default tag).</param>
  436. public override void RecalculateCell (int x, int z, bool resetPenalties = true, bool resetTags = true) {
  437. // Cosine of the maximum slope angle
  438. float cosAngle = Mathf.Cos(maxSlope*Mathf.Deg2Rad);
  439. // Get samples of points when firing a ray from the sky down towards the ground
  440. // The cell sampler handles some nice things like merging spans that are really close together
  441. int numHeightSamples;
  442. var heightSamples = SampleHeights(collision, mergeSpanRange, transform.Transform(new Vector3(x+0.5F, 0, z+0.5F)), out numHeightSamples);
  443. if (numHeightSamples > layerCount) {
  444. if (numHeightSamples > LevelGridNode.MaxLayerCount) {
  445. Debug.LogError("Too many layers, a maximum of " + LevelGridNode.MaxLayerCount + " are allowed (required " + numHeightSamples + ")");
  446. return;
  447. }
  448. AddLayers(numHeightSamples - layerCount);
  449. }
  450. int layerIndex = 0;
  451. for (; layerIndex < numHeightSamples; layerIndex++) {
  452. var sample = heightSamples[layerIndex];
  453. var index = z*width+x + width*depth*layerIndex;
  454. var node = nodes[index] as LevelGridNode;
  455. bool isNewNode = node == null;
  456. if (isNewNode) {
  457. // Destroy previous node
  458. if (nodes[index] != null) {
  459. nodes[index].Destroy();
  460. }
  461. // Create a new node
  462. node = new LevelGridNode(active);
  463. nodes[index] = node;
  464. node.NodeInGridIndex = z*width+x;
  465. node.LayerCoordinateInGrid = layerIndex;
  466. node.GraphIndex = graphIndex;
  467. }
  468. #if ASTAR_SET_LEVELGRIDNODE_HEIGHT
  469. node.height = sample.height;
  470. #endif
  471. node.position = (Int3)sample.position;
  472. node.Walkable = sample.walkable;
  473. node.WalkableErosion = node.Walkable;
  474. if (isNewNode || resetPenalties) {
  475. node.Penalty = initialPenalty;
  476. if (penaltyPosition) {
  477. node.Penalty += (uint)Mathf.RoundToInt((node.position.y-penaltyPositionOffset)*penaltyPositionFactor);
  478. }
  479. }
  480. if (isNewNode || resetTags) {
  481. node.Tag = 0;
  482. }
  483. // Adjust penalty based on the surface slope
  484. if (sample.hit.normal != Vector3.zero && (penaltyAngle || cosAngle > 0.0001f)) {
  485. // Take the dot product to find out the cosinus of the angle it has (faster than Vector3.Angle)
  486. float angle = Vector3.Dot(sample.hit.normal.normalized, collision.up);
  487. // Add penalty based on normal
  488. if (resetTags && penaltyAngle) {
  489. node.Penalty += (uint)Mathf.RoundToInt((1F-angle)*penaltyAngleFactor);
  490. }
  491. // Check if the slope is flat enough to stand on
  492. if (angle < cosAngle) {
  493. node.Walkable = false;
  494. }
  495. }
  496. if (sample.height < characterHeight) {
  497. node.Walkable = false;
  498. }
  499. node.WalkableErosion = node.Walkable;
  500. }
  501. // Clear unused nodes
  502. for (; layerIndex < layerCount; layerIndex++) {
  503. var index = z*width+x + width*depth*layerIndex;
  504. if (nodes[index] != null) nodes[index].Destroy();
  505. nodes[index] = null;
  506. }
  507. }
  508. /// <summary>Increases the capacity of the nodes array to hold more layers</summary>
  509. void AddLayers (int count) {
  510. int newLayerCount = layerCount + count;
  511. if (newLayerCount > LevelGridNode.MaxLayerCount) {
  512. Debug.LogError("Too many layers, a maximum of " + LevelGridNode.MaxLayerCount + " are allowed (required "+newLayerCount+")");
  513. return;
  514. }
  515. GridNodeBase[] tmp = nodes;
  516. nodes = new GridNodeBase[width*depth*newLayerCount];
  517. tmp.CopyTo(nodes, 0);
  518. layerCount = newLayerCount;
  519. }
  520. protected override bool ErosionAnyFalseConnections (GraphNode baseNode) {
  521. var node = baseNode as LevelGridNode;
  522. if (neighbours == NumNeighbours.Six) {
  523. // Check the 6 hexagonal connections
  524. for (int i = 0; i < 6; i++) {
  525. if (!node.HasConnectionInDirection(hexagonNeighbourIndices[i])) {
  526. return true;
  527. }
  528. }
  529. } else {
  530. // Check the four axis aligned connections
  531. for (int i = 0; i < 4; i++) {
  532. if (!node.HasConnectionInDirection(i)) {
  533. return true;
  534. }
  535. }
  536. }
  537. return false;
  538. }
  539. public override void CalculateConnections (GridNodeBase baseNode) {
  540. var node = baseNode as LevelGridNode;
  541. CalculateConnections(node.XCoordinateInGrid, node.ZCoordinateInGrid, node.LayerCoordinateInGrid);
  542. }
  543. /// <summary>
  544. /// Calculates the layered grid graph connections for a single node.
  545. /// Deprecated: Use CalculateConnections(x,z,layerIndex) or CalculateConnections(node) instead
  546. /// </summary>
  547. [System.Obsolete("Use CalculateConnections(x,z,layerIndex) or CalculateConnections(node) instead")]
  548. public void CalculateConnections (int x, int z, int layerIndex, LevelGridNode node) {
  549. CalculateConnections(x, z, layerIndex);
  550. }
  551. /// <summary>Calculates connections for all nodes in a cell (there may be multiple layers of nodes)</summary>
  552. public override void CalculateConnections (int x, int z) {
  553. for (int i = 0; i < layerCount; i++) {
  554. CalculateConnections(x, z, i);
  555. }
  556. }
  557. /// <summary>Calculates the layered grid graph connections for a single node</summary>
  558. public void CalculateConnections (int x, int z, int layerIndex) {
  559. var node = nodes[z*width+x + width*depth*layerIndex] as LevelGridNode;
  560. if (node == null) return;
  561. node.ResetAllGridConnections();
  562. if (!node.Walkable) {
  563. return;
  564. }
  565. var nodePos = (Vector3)node.position;
  566. var up = transform.WorldUpAtGraphPosition(nodePos);
  567. var ourY = Vector3.Dot(nodePos, up);
  568. float height;
  569. if (layerIndex == layerCount-1 || nodes[node.NodeInGridIndex + width*depth*(layerIndex+1)] == null) {
  570. height = float.PositiveInfinity;
  571. } else {
  572. height = System.Math.Abs(ourY - Vector3.Dot((Vector3)nodes[node.NodeInGridIndex+width*depth*(layerIndex+1)].position, up));
  573. }
  574. for (int dir = 0; dir < 4; dir++) {
  575. int nx = x + neighbourXOffsets[dir];
  576. int nz = z + neighbourZOffsets[dir];
  577. // Check for out-of-bounds
  578. if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
  579. continue;
  580. }
  581. // Calculate new index
  582. int nIndex = nz*width+nx;
  583. int conn = LevelGridNode.NoConnection;
  584. for (int i = 0; i < layerCount; i++) {
  585. GraphNode other = nodes[nIndex + width*depth*i];
  586. if (other != null && other.Walkable) {
  587. float otherHeight;
  588. var otherY = Vector3.Dot((Vector3)other.position, up);
  589. // Is there a node above this one
  590. if (i == layerCount-1 || nodes[nIndex+width*depth*(i+1)] == null) {
  591. otherHeight = float.PositiveInfinity;
  592. } else {
  593. otherHeight = System.Math.Abs(otherY - Vector3.Dot((Vector3)nodes[nIndex+width*depth*(i+1)].position, up));
  594. }
  595. float bottom = Mathf.Max(otherY, ourY);
  596. float top = Mathf.Min(otherY+otherHeight, ourY+height);
  597. float dist = top-bottom;
  598. if (dist >= characterHeight && Mathf.Abs(otherY - ourY) <= maxClimb) {
  599. conn = i;
  600. }
  601. }
  602. }
  603. node.SetConnectionValue(dir, conn);
  604. }
  605. }
  606. public override NNInfoInternal GetNearest (Vector3 position, NNConstraint constraint, GraphNode hint) {
  607. if (nodes == null || depth*width*layerCount != nodes.Length) {
  608. //Debug.LogError ("NavGraph hasn't been generated yet");
  609. return new NNInfoInternal();
  610. }
  611. var graphPosition = transform.InverseTransform(position);
  612. float xf = graphPosition.x;
  613. float zf = graphPosition.z;
  614. int x = Mathf.Clamp((int)xf, 0, width-1);
  615. int z = Mathf.Clamp((int)zf, 0, depth-1);
  616. var minNode = GetNearestNode(position, x, z, null);
  617. var nn = new NNInfoInternal(minNode);
  618. float y = transform.InverseTransform((Vector3)minNode.position).y;
  619. nn.clampedPosition = transform.Transform(new Vector3(Mathf.Clamp(xf, x, x+1f), y, Mathf.Clamp(zf, z, z+1f)));
  620. return nn;
  621. }
  622. protected override GridNodeBase GetNearestFromGraphSpace (Vector3 positionGraphSpace) {
  623. if (nodes == null || depth*width*layerCount != nodes.Length) {
  624. return null;
  625. }
  626. float xf = positionGraphSpace.x;
  627. float zf = positionGraphSpace.z;
  628. int x = Mathf.Clamp((int)xf, 0, width-1);
  629. int z = Mathf.Clamp((int)zf, 0, depth-1);
  630. var worldPos = transform.Transform(positionGraphSpace);
  631. return GetNearestNode(worldPos, x, z, null);
  632. }
  633. private GridNodeBase GetNearestNode (Vector3 position, int x, int z, NNConstraint constraint) {
  634. int index = width*z+x;
  635. float minDist = float.PositiveInfinity;
  636. GridNodeBase minNode = null;
  637. for (int i = 0; i < layerCount; i++) {
  638. var node = nodes[index + width*depth*i];
  639. if (node != null) {
  640. float dist = ((Vector3)node.position - position).sqrMagnitude;
  641. if (dist < minDist && (constraint == null || constraint.Suitable(node))) {
  642. minDist = dist;
  643. minNode = node;
  644. }
  645. }
  646. }
  647. return minNode;
  648. }
  649. /// <summary>
  650. /// Returns if node is connected to it's neighbour in the specified direction.
  651. /// Deprecated: Use node.HasConnectionInDirection instead
  652. /// </summary>
  653. [System.Obsolete("Use node.HasConnectionInDirection instead")]
  654. public static bool CheckConnection (LevelGridNode node, int dir) {
  655. return node.HasConnectionInDirection(dir);
  656. }
  657. protected override void SerializeExtraInfo (GraphSerializationContext ctx) {
  658. if (nodes == null) {
  659. ctx.writer.Write(-1);
  660. return;
  661. }
  662. ctx.writer.Write(nodes.Length);
  663. for (int i = 0; i < nodes.Length; i++) {
  664. if (nodes[i] == null) {
  665. ctx.writer.Write(-1);
  666. } else {
  667. ctx.writer.Write(0);
  668. nodes[i].SerializeNode(ctx);
  669. }
  670. }
  671. }
  672. protected override void DeserializeExtraInfo (GraphSerializationContext ctx) {
  673. int count = ctx.reader.ReadInt32();
  674. if (count == -1) {
  675. nodes = null;
  676. return;
  677. }
  678. nodes = new LevelGridNode[count];
  679. for (int i = 0; i < nodes.Length; i++) {
  680. if (ctx.reader.ReadInt32() != -1) {
  681. nodes[i] = new LevelGridNode(active);
  682. nodes[i].DeserializeNode(ctx);
  683. } else {
  684. nodes[i] = null;
  685. }
  686. }
  687. }
  688. protected override void PostDeserialization (GraphSerializationContext ctx) {
  689. UpdateTransform();
  690. lastScannedWidth = width;
  691. lastScannedDepth = depth;
  692. SetUpOffsetsAndCosts();
  693. LevelGridNode.SetGridGraph((int)graphIndex, this);
  694. if (nodes == null || nodes.Length == 0) return;
  695. if (width*depth*layerCount != nodes.Length) {
  696. Debug.LogError("Node data did not match with bounds data. Probably a change to the bounds/width/depth data was made after scanning the graph just prior to saving it. Nodes will be discarded");
  697. nodes = new LevelGridNode[0];
  698. return;
  699. }
  700. for (int i = 0; i < layerCount; i++) {
  701. for (int z = 0; z < depth; z++) {
  702. for (int x = 0; x < width; x++) {
  703. var node = nodes[z*width+x + width*depth*i] as LevelGridNode;
  704. if (node == null) {
  705. continue;
  706. }
  707. node.NodeInGridIndex = z*width+x;
  708. node.LayerCoordinateInGrid = i;
  709. }
  710. }
  711. }
  712. }
  713. }
  714. /// <summary>
  715. /// Describes a single node for the LayerGridGraph.
  716. /// Works almost the same as a grid node, except that it also stores to which layer the connections go to
  717. /// </summary>
  718. public class LevelGridNode : GridNodeBase {
  719. public LevelGridNode (AstarPath astar) : base(astar) {
  720. }
  721. private static LayerGridGraph[] _gridGraphs = new LayerGridGraph[0];
  722. public static LayerGridGraph GetGridGraph (uint graphIndex) { return _gridGraphs[(int)graphIndex]; }
  723. public static void SetGridGraph (int graphIndex, LayerGridGraph graph) {
  724. // LayeredGridGraphs also show up in the grid graph list
  725. // This is required by e.g the XCoordinateInGrid properties
  726. GridNode.SetGridGraph(graphIndex, graph);
  727. if (_gridGraphs.Length <= graphIndex) {
  728. var newGraphs = new LayerGridGraph[graphIndex+1];
  729. for (int i = 0; i < _gridGraphs.Length; i++) newGraphs[i] = _gridGraphs[i];
  730. _gridGraphs = newGraphs;
  731. }
  732. _gridGraphs[graphIndex] = graph;
  733. }
  734. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  735. public uint gridConnections;
  736. #else
  737. public ulong gridConnections;
  738. #endif
  739. #if ASTAR_SET_LEVELGRIDNODE_HEIGHT
  740. public float height;
  741. #endif
  742. protected static LayerGridGraph[] gridGraphs;
  743. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  744. public const int NoConnection = 0xF;
  745. private const int ConnectionMask = 0xF;
  746. private const int ConnectionStride = 4;
  747. #else
  748. public const int NoConnection = 0xFF;
  749. public const int ConnectionMask = 0xFF;
  750. private const int ConnectionStride = 8;
  751. #endif
  752. public const int MaxLayerCount = ConnectionMask;
  753. /// <summary>Removes all grid connections from this node</summary>
  754. public void ResetAllGridConnections () {
  755. unchecked {
  756. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  757. gridConnections = (uint)-1;
  758. #else
  759. gridConnections = (ulong)-1;
  760. #endif
  761. }
  762. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  763. }
  764. /// <summary>Does this node have any grid connections</summary>
  765. public bool HasAnyGridConnections () {
  766. unchecked {
  767. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  768. return gridConnections != (uint)-1;
  769. #else
  770. return gridConnections != (ulong)-1;
  771. #endif
  772. }
  773. }
  774. public override bool HasConnectionsToAllEightNeighbours {
  775. get {
  776. // Layered grid graphs only support 4 neighbours
  777. return false;
  778. }
  779. }
  780. /// <summary>
  781. /// Layer coordinate of the node in the grid.
  782. /// If there are multiple nodes in the same (x,z) cell, then they will be stored in different layers.
  783. /// Together with NodeInGridIndex, you can look up the node in the nodes array
  784. /// <code>
  785. /// int index = node.NodeInGridIndex + node.LayerCoordinateInGrid * graph.width * graph.depth;
  786. /// Assert(node == graph.nodes[index]);
  787. /// </code>
  788. ///
  789. /// See: XCoordInGrid
  790. /// See: ZCoordInGrid
  791. /// See: NodeInGridIndex
  792. /// </summary>
  793. public int LayerCoordinateInGrid { get { return nodeInGridIndex >> NodeInGridIndexLayerOffset; } set { nodeInGridIndex = (nodeInGridIndex & NodeInGridIndexMask) | (value << NodeInGridIndexLayerOffset); } }
  794. public void SetPosition (Int3 position) {
  795. this.position = position;
  796. }
  797. public override int GetGizmoHashCode () {
  798. return base.GetGizmoHashCode() ^ (int)(805306457UL * gridConnections);
  799. }
  800. public override GridNodeBase GetNeighbourAlongDirection (int direction) {
  801. int conn = GetConnectionValue(direction);
  802. if (conn != NoConnection) {
  803. LayerGridGraph graph = GetGridGraph(GraphIndex);
  804. return graph.nodes[NodeInGridIndex+graph.neighbourOffsets[direction] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
  805. }
  806. return null;
  807. }
  808. public override void ClearConnections (bool alsoReverse) {
  809. if (alsoReverse) {
  810. LayerGridGraph graph = GetGridGraph(GraphIndex);
  811. int[] neighbourOffsets = graph.neighbourOffsets;
  812. GridNodeBase[] nodes = graph.nodes;
  813. for (int i = 0; i < 4; i++) {
  814. int conn = GetConnectionValue(i);
  815. if (conn != LevelGridNode.NoConnection) {
  816. LevelGridNode other = nodes[NodeInGridIndex+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn] as LevelGridNode;
  817. if (other != null) {
  818. // Remove reverse connection
  819. other.SetConnectionValue((i + 2) % 4, NoConnection);
  820. }
  821. }
  822. }
  823. }
  824. ResetAllGridConnections();
  825. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  826. base.ClearConnections(alsoReverse);
  827. #endif
  828. }
  829. public override void GetConnections (System.Action<GraphNode> action) {
  830. LayerGridGraph graph = GetGridGraph(GraphIndex);
  831. int[] neighbourOffsets = graph.neighbourOffsets;
  832. GridNodeBase[] nodes = graph.nodes;
  833. int index = NodeInGridIndex;
  834. for (int i = 0; i < 4; i++) {
  835. int conn = GetConnectionValue(i);
  836. if (conn != LevelGridNode.NoConnection) {
  837. GraphNode other = nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
  838. if (other != null) action(other);
  839. }
  840. }
  841. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  842. base.GetConnections(action);
  843. #endif
  844. }
  845. /// <summary>
  846. /// Is there a grid connection in that direction.
  847. ///
  848. /// Deprecated: Use <see cref="HasConnectionInDirection"/> instead
  849. /// </summary>
  850. [System.Obsolete("Use HasConnectionInDirection instead")]
  851. public bool GetConnection (int i) {
  852. return ((gridConnections >> i*ConnectionStride) & ConnectionMask) != NoConnection;
  853. }
  854. public override bool HasConnectionInDirection (int direction) {
  855. return ((gridConnections >> direction*ConnectionStride) & ConnectionMask) != NoConnection;
  856. }
  857. /// <summary>Set which layer a grid connection goes to.</summary>
  858. /// <param name="dir">Direction for the connection.</param>
  859. /// <param name="value">The layer of the connected node or #NoConnection if there should be no connection in that direction.</param>
  860. public void SetConnectionValue (int dir, int value) {
  861. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  862. gridConnections = gridConnections & ~(((uint)NoConnection << dir*ConnectionStride)) | ((uint)value << dir*ConnectionStride);
  863. #else
  864. gridConnections = gridConnections & ~(((ulong)NoConnection << dir*ConnectionStride)) | ((ulong)value << dir*ConnectionStride);
  865. #endif
  866. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  867. }
  868. /// <summary>
  869. /// Which layer a grid connection goes to.
  870. /// Returns: The layer of the connected node or <see cref="NoConnection"/> if there is no connection in that direction.
  871. /// </summary>
  872. /// <param name="dir">Direction for the connection.</param>
  873. public int GetConnectionValue (int dir) {
  874. return (int)((gridConnections >> dir*ConnectionStride) & ConnectionMask);
  875. }
  876. public override void AddConnection (GraphNode node, uint cost) {
  877. // In case the node was already added as an internal grid connection,
  878. // we need to remove that connection before we insert it as a custom connection.
  879. // Using a custom connection is necessary because it has a custom cost.
  880. if (node is LevelGridNode gn && gn.GraphIndex == GraphIndex) {
  881. RemoveGridConnection(gn);
  882. }
  883. base.AddConnection(node, cost);
  884. }
  885. public override void RemoveConnection (GraphNode node) {
  886. base.RemoveConnection(node);
  887. // If the node is a grid node on the same graph, it might be added as an internal connection and not a custom one.
  888. if (node is LevelGridNode gn && gn.GraphIndex == GraphIndex) {
  889. RemoveGridConnection(gn);
  890. }
  891. }
  892. /// <summary>
  893. /// Removes a connection from the internal grid connections, not the list of custom connections.
  894. /// See: SetConnectionValue
  895. /// </summary>
  896. protected void RemoveGridConnection (LevelGridNode node) {
  897. var nodeIndex = NodeInGridIndex;
  898. var gg = GetGridGraph(GraphIndex);
  899. for (int i = 0; i < 8; i++) {
  900. if (nodeIndex + gg.neighbourOffsets[i] == node.NodeInGridIndex && GetNeighbourAlongDirection(i) == node) {
  901. SetConnectionValue(i, NoConnection);
  902. break;
  903. }
  904. }
  905. }
  906. public override bool GetPortal (GraphNode other, List<Vector3> left, List<Vector3> right, bool backwards) {
  907. if (backwards) return true;
  908. LayerGridGraph graph = GetGridGraph(GraphIndex);
  909. int[] neighbourOffsets = graph.neighbourOffsets;
  910. GridNodeBase[] nodes = graph.nodes;
  911. int index = NodeInGridIndex;
  912. for (int i = 0; i < 4; i++) {
  913. int conn = GetConnectionValue(i);
  914. if (conn != LevelGridNode.NoConnection) {
  915. if (other == nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn]) {
  916. Vector3 middle = ((Vector3)(position + other.position))*0.5f;
  917. Vector3 cross = Vector3.Cross(graph.collision.up, (Vector3)(other.position-position));
  918. cross.Normalize();
  919. cross *= graph.nodeSize*0.5f;
  920. left.Add(middle - cross);
  921. right.Add(middle + cross);
  922. return true;
  923. }
  924. }
  925. }
  926. return false;
  927. }
  928. public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
  929. handler.heap.Add(pathNode);
  930. pathNode.UpdateG(path);
  931. LayerGridGraph graph = GetGridGraph(GraphIndex);
  932. int[] neighbourOffsets = graph.neighbourOffsets;
  933. GridNodeBase[] nodes = graph.nodes;
  934. int index = NodeInGridIndex;
  935. for (int i = 0; i < 4; i++) {
  936. int conn = GetConnectionValue(i);
  937. if (conn != LevelGridNode.NoConnection) {
  938. var other = nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
  939. PathNode otherPN = handler.GetPathNode(other);
  940. if (otherPN != null && otherPN.parent == pathNode && otherPN.pathID == handler.PathID) {
  941. other.UpdateRecursiveG(path, otherPN, handler);
  942. }
  943. }
  944. }
  945. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  946. base.UpdateRecursiveG(path, pathNode, handler);
  947. #endif
  948. }
  949. public override void Open (Path path, PathNode pathNode, PathHandler handler) {
  950. LayerGridGraph graph = GetGridGraph(GraphIndex);
  951. int[] neighbourOffsets = graph.neighbourOffsets;
  952. uint[] neighbourCosts = graph.neighbourCosts;
  953. GridNodeBase[] nodes = graph.nodes;
  954. int index = NodeInGridIndex;
  955. for (int i = 0; i < 4; i++) {
  956. int conn = GetConnectionValue(i);
  957. if (conn != LevelGridNode.NoConnection) {
  958. GraphNode other = nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
  959. if (!path.CanTraverse(other)) {
  960. continue;
  961. }
  962. PathNode otherPN = handler.GetPathNode(other);
  963. if (otherPN.pathID != handler.PathID) {
  964. otherPN.parent = pathNode;
  965. otherPN.pathID = handler.PathID;
  966. otherPN.cost = neighbourCosts[i];
  967. otherPN.H = path.CalculateHScore(other);
  968. otherPN.UpdateG(path);
  969. handler.heap.Add(otherPN);
  970. } else {
  971. //If not we can test if the path from the current node to this one is a better one then the one already used
  972. uint tmpCost = neighbourCosts[i];
  973. #if ASTAR_NO_TRAVERSAL_COST
  974. if (pathNode.G + tmpCost < otherPN.G)
  975. #else
  976. if (pathNode.G + tmpCost + path.GetTraversalCost(other) < otherPN.G)
  977. #endif
  978. {
  979. otherPN.cost = tmpCost;
  980. otherPN.parent = pathNode;
  981. other.UpdateRecursiveG(path, otherPN, handler);
  982. }
  983. }
  984. }
  985. }
  986. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  987. base.Open(path, pathNode, handler);
  988. #endif
  989. }
  990. public override Vector3 ClosestPointOnNode (Vector3 p) {
  991. var gg = GetGridGraph(GraphIndex);
  992. // Convert to graph space
  993. p = gg.transform.InverseTransform(p);
  994. // Calculate graph position of this node
  995. int x = this.XCoordinateInGrid;
  996. int z = this.ZCoordinateInGrid;
  997. // Handle the y coordinate separately
  998. float y = gg.transform.InverseTransform((Vector3)position).y;
  999. var closestInGraphSpace = new Vector3(Mathf.Clamp(p.x, x, x+1f), y, Mathf.Clamp(p.z, z, z+1f));
  1000. // Convert to world space
  1001. return gg.transform.Transform(closestInGraphSpace);
  1002. }
  1003. public override void SerializeNode (GraphSerializationContext ctx) {
  1004. base.SerializeNode(ctx);
  1005. ctx.SerializeInt3(position);
  1006. ctx.writer.Write(gridFlags);
  1007. // gridConnections are now always serialized as 64 bits for easier compatibility handling
  1008. ctx.writer.Write((ulong)gridConnections);
  1009. }
  1010. public override void DeserializeNode (GraphSerializationContext ctx) {
  1011. base.DeserializeNode(ctx);
  1012. position = ctx.DeserializeInt3();
  1013. gridFlags = ctx.reader.ReadUInt16();
  1014. if (ctx.meta.version < AstarSerializer.V3_9_0) {
  1015. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  1016. // Set the upper 16 bits for compatibility
  1017. gridConnections = ctx.reader.ReadUInt16() | 0xFFFF0000U;
  1018. #else
  1019. // Set the upper 32 bits for compatibility
  1020. gridConnections = ctx.reader.ReadUInt32() | 0xFFFFFFFF00000000UL;
  1021. #endif
  1022. } else {
  1023. #if ASTAR_LEVELGRIDNODE_FEW_LAYERS
  1024. gridConnections = (uint)ctx.reader.ReadUInt64();
  1025. #else
  1026. gridConnections = ctx.reader.ReadUInt64();
  1027. #endif
  1028. }
  1029. }
  1030. }
  1031. /// <summary>
  1032. /// GraphUpdateObject with more settings for the LayerGridGraph.
  1033. /// See: Pathfinding.GraphUpdateObject
  1034. /// See: Pathfinding.LayerGridGraph
  1035. /// </summary>
  1036. public class LayerGridGraphUpdate : GraphUpdateObject {
  1037. /// <summary>Recalculate nodes in the graph. Nodes might be created, moved or destroyed depending on how the world has changed.</summary>
  1038. public bool recalculateNodes;
  1039. /// <summary>If true, nodes will be reused. This can be used to preserve e.g penalty values when recalculating</summary>
  1040. public bool preserveExistingNodes = true;
  1041. }
  1042. }
  1043. #endif