GridNode.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. using System.Collections.Generic;
  2. using Pathfinding.Serialization;
  3. using UnityEngine;
  4. namespace Pathfinding {
  5. /// <summary>Node used for the GridGraph</summary>
  6. public class GridNode : GridNodeBase {
  7. public GridNode (AstarPath astar) : base(astar) {
  8. }
  9. #if !ASTAR_NO_GRID_GRAPH
  10. private static GridGraph[] _gridGraphs = new GridGraph[0];
  11. public static GridGraph GetGridGraph (uint graphIndex) { return _gridGraphs[(int)graphIndex]; }
  12. public static void SetGridGraph (int graphIndex, GridGraph graph) {
  13. if (_gridGraphs.Length <= graphIndex) {
  14. var gg = new GridGraph[graphIndex+1];
  15. for (int i = 0; i < _gridGraphs.Length; i++) gg[i] = _gridGraphs[i];
  16. _gridGraphs = gg;
  17. }
  18. _gridGraphs[graphIndex] = graph;
  19. }
  20. public static void ClearGridGraph (int graphIndex, GridGraph graph) {
  21. if (graphIndex < _gridGraphs.Length && _gridGraphs[graphIndex] == graph) {
  22. _gridGraphs[graphIndex] = null;
  23. }
  24. }
  25. /// <summary>Internal use only</summary>
  26. internal ushort InternalGridFlags {
  27. get { return gridFlags; }
  28. set { gridFlags = value; }
  29. }
  30. const int GridFlagsConnectionOffset = 0;
  31. const int GridFlagsConnectionBit0 = 1 << GridFlagsConnectionOffset;
  32. const int GridFlagsConnectionMask = 0xFF << GridFlagsConnectionOffset;
  33. const int GridFlagsEdgeNodeOffset = 10;
  34. const int GridFlagsEdgeNodeMask = 1 << GridFlagsEdgeNodeOffset;
  35. public override bool HasConnectionsToAllEightNeighbours {
  36. get {
  37. return (InternalGridFlags & GridFlagsConnectionMask) == GridFlagsConnectionMask;
  38. }
  39. }
  40. /// <summary>
  41. /// True if the node has a connection in the specified direction.
  42. /// The dir parameter corresponds to directions in the grid as:
  43. /// <code>
  44. /// Z
  45. /// |
  46. /// |
  47. ///
  48. /// 6 2 5
  49. /// \ | /
  50. /// -- 3 - X - 1 ----- X
  51. /// / | \
  52. /// 7 0 4
  53. ///
  54. /// |
  55. /// |
  56. /// </code>
  57. ///
  58. /// See: SetConnectionInternal
  59. /// </summary>
  60. public override bool HasConnectionInDirection (int dir) {
  61. return (gridFlags >> dir & GridFlagsConnectionBit0) != 0;
  62. }
  63. /// <summary>
  64. /// True if the node has a connection in the specified direction.
  65. /// Deprecated: Use HasConnectionInDirection
  66. /// </summary>
  67. [System.Obsolete("Use HasConnectionInDirection")]
  68. public bool GetConnectionInternal (int dir) {
  69. return HasConnectionInDirection(dir);
  70. }
  71. /// <summary>
  72. /// Enables or disables a connection in a specified direction on the graph.
  73. /// See: HasConnectionInDirection
  74. /// </summary>
  75. public void SetConnectionInternal (int dir, bool value) {
  76. // Set bit number #dir to 1 or 0 depending on #value
  77. unchecked { gridFlags = (ushort)(gridFlags & ~((ushort)1 << GridFlagsConnectionOffset << dir) | (value ? (ushort)1 : (ushort)0) << GridFlagsConnectionOffset << dir); }
  78. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  79. }
  80. /// <summary>
  81. /// Sets the state of all grid connections.
  82. ///
  83. /// See: SetConnectionInternal
  84. /// </summary>
  85. /// <param name="connections">a bitmask of the connections (bit 0 is the first connection, bit 1 the second connection, etc.).</param>
  86. public void SetAllConnectionInternal (int connections) {
  87. unchecked { gridFlags = (ushort)((gridFlags & ~GridFlagsConnectionMask) | (connections << GridFlagsConnectionOffset)); }
  88. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  89. }
  90. /// <summary>
  91. /// Disables all grid connections from this node.
  92. /// Note: Other nodes might still be able to get to this node.
  93. /// Therefore it is recommended to also disable the relevant connections on adjacent nodes.
  94. /// </summary>
  95. public void ResetConnectionsInternal () {
  96. unchecked {
  97. gridFlags = (ushort)(gridFlags & ~GridFlagsConnectionMask);
  98. }
  99. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  100. }
  101. /// <summary>
  102. /// Work in progress for a feature that required info about which nodes were at the border of the graph.
  103. /// Note: This property is not functional at the moment.
  104. /// </summary>
  105. public bool EdgeNode {
  106. get {
  107. return (gridFlags & GridFlagsEdgeNodeMask) != 0;
  108. }
  109. set {
  110. unchecked { gridFlags = (ushort)(gridFlags & ~GridFlagsEdgeNodeMask | (value ? GridFlagsEdgeNodeMask : 0)); }
  111. }
  112. }
  113. public override GridNodeBase GetNeighbourAlongDirection (int direction) {
  114. if (HasConnectionInDirection(direction)) {
  115. GridGraph gg = GetGridGraph(GraphIndex);
  116. return gg.nodes[NodeInGridIndex+gg.neighbourOffsets[direction]];
  117. }
  118. return null;
  119. }
  120. public override void ClearConnections (bool alsoReverse) {
  121. if (alsoReverse) {
  122. // Note: This assumes that all connections are bidirectional
  123. // which should hold for all grid graphs unless some custom code has been added
  124. for (int i = 0; i < 8; i++) {
  125. var other = GetNeighbourAlongDirection(i) as GridNode;
  126. if (other != null) {
  127. // Remove reverse connection. See doc for GridGraph.neighbourOffsets to see which indices are used for what.
  128. other.SetConnectionInternal(i < 4 ? ((i + 2) % 4) : (((i-2) % 4) + 4), false);
  129. }
  130. }
  131. }
  132. ResetConnectionsInternal();
  133. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  134. base.ClearConnections(alsoReverse);
  135. #endif
  136. }
  137. public override void GetConnections (System.Action<GraphNode> action) {
  138. GridGraph gg = GetGridGraph(GraphIndex);
  139. int[] neighbourOffsets = gg.neighbourOffsets;
  140. var nodes = gg.nodes;
  141. for (int i = 0; i < 8; i++) {
  142. if (HasConnectionInDirection(i)) {
  143. var other = nodes[NodeInGridIndex + neighbourOffsets[i]];
  144. if (other != null) action(other);
  145. }
  146. }
  147. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  148. base.GetConnections(action);
  149. #endif
  150. }
  151. public override Vector3 ClosestPointOnNode (Vector3 p) {
  152. var gg = GetGridGraph(GraphIndex);
  153. // Convert to graph space
  154. p = gg.transform.InverseTransform(p);
  155. // Calculate graph position of this node
  156. int x = NodeInGridIndex % gg.width;
  157. int z = NodeInGridIndex / gg.width;
  158. // Handle the y coordinate separately
  159. float y = gg.transform.InverseTransform((Vector3)position).y;
  160. var closestInGraphSpace = new Vector3(Mathf.Clamp(p.x, x, x+1f), y, Mathf.Clamp(p.z, z, z+1f));
  161. // Convert to world space
  162. return gg.transform.Transform(closestInGraphSpace);
  163. }
  164. public override bool GetPortal (GraphNode other, List<Vector3> left, List<Vector3> right, bool backwards) {
  165. if (backwards) return true;
  166. GridGraph gg = GetGridGraph(GraphIndex);
  167. int[] neighbourOffsets = gg.neighbourOffsets;
  168. var nodes = gg.nodes;
  169. for (int i = 0; i < 4; i++) {
  170. if (HasConnectionInDirection(i) && other == nodes[NodeInGridIndex + neighbourOffsets[i]]) {
  171. Vector3 middle = ((Vector3)(position + other.position))*0.5f;
  172. Vector3 cross = Vector3.Cross(gg.collision.up, (Vector3)(other.position-position));
  173. cross.Normalize();
  174. cross *= gg.nodeSize*0.5f;
  175. left.Add(middle - cross);
  176. right.Add(middle + cross);
  177. return true;
  178. }
  179. }
  180. for (int i = 4; i < 8; i++) {
  181. if (HasConnectionInDirection(i) && other == nodes[NodeInGridIndex + neighbourOffsets[i]]) {
  182. bool rClear = false;
  183. bool lClear = false;
  184. if (HasConnectionInDirection(i-4)) {
  185. var n2 = nodes[NodeInGridIndex + neighbourOffsets[i-4]];
  186. if (n2.Walkable && n2.HasConnectionInDirection((i-4+1)%4)) {
  187. rClear = true;
  188. }
  189. }
  190. if (HasConnectionInDirection((i-4+1)%4)) {
  191. var n2 = nodes[NodeInGridIndex + neighbourOffsets[(i-4+1)%4]];
  192. if (n2.Walkable && n2.HasConnectionInDirection(i-4)) {
  193. lClear = true;
  194. }
  195. }
  196. Vector3 middle = ((Vector3)(position + other.position))*0.5f;
  197. Vector3 cross = Vector3.Cross(gg.collision.up, (Vector3)(other.position-position));
  198. cross.Normalize();
  199. cross *= gg.nodeSize*1.4142f;
  200. left.Add(middle - (lClear ? cross : Vector3.zero));
  201. right.Add(middle + (rClear ? cross : Vector3.zero));
  202. return true;
  203. }
  204. }
  205. return false;
  206. }
  207. public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
  208. GridGraph gg = GetGridGraph(GraphIndex);
  209. int[] neighbourOffsets = gg.neighbourOffsets;
  210. var nodes = gg.nodes;
  211. pathNode.UpdateG(path);
  212. handler.heap.Add(pathNode);
  213. ushort pid = handler.PathID;
  214. var index = NodeInGridIndex;
  215. for (int i = 0; i < 8; i++) {
  216. if (HasConnectionInDirection(i)) {
  217. var other = nodes[index + neighbourOffsets[i]];
  218. PathNode otherPN = handler.GetPathNode(other);
  219. if (otherPN.parent == pathNode && otherPN.pathID == pid) other.UpdateRecursiveG(path, otherPN, handler);
  220. }
  221. }
  222. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  223. base.UpdateRecursiveG(path, pathNode, handler);
  224. #endif
  225. }
  226. #if ASTAR_JPS
  227. /*
  228. * Non Cyclic
  229. * [0] = -Y
  230. * [1] = +X
  231. * [2] = +Y
  232. * [3] = -X
  233. * [4] = -Y+X
  234. * [5] = +Y+X
  235. * [6] = +Y-X
  236. * [7] = -Y-X
  237. *
  238. * Cyclic
  239. * [0] = -X
  240. * [1] = -X+Y
  241. * [2] = +Y
  242. * [3] = +X+Y
  243. * [4] = +X
  244. * [5] = +X-Y
  245. * [6] = -Y
  246. * [7] = -Y-X
  247. */
  248. static byte[] JPSForced = {
  249. 0xFF, // Shouldn't really happen
  250. 0,
  251. (1<<3),
  252. 0,
  253. 0,
  254. 0,
  255. (1<<5),
  256. 0
  257. };
  258. static byte[] JPSForcedDiagonal = {
  259. 0xFF, // Shouldn't really happen
  260. (1<<2),
  261. 0,
  262. 0,
  263. 0,
  264. 0,
  265. 0,
  266. (1<<6)
  267. };
  268. /// <summary>
  269. /// Permutation of the standard grid node neighbour order to put them on a clockwise cycle around the node.
  270. /// Enables easier math in some cases
  271. /// </summary>
  272. static int[] JPSCyclic = {
  273. 6,
  274. 4,
  275. 2,
  276. 0,
  277. 5,
  278. 3,
  279. 1,
  280. 7
  281. };
  282. /// <summary>Inverse permutation of <see cref="JPSCyclic"/></summary>
  283. static int[] JPSInverseCyclic = {
  284. 3, // 0
  285. 6, // 1
  286. 2, // 2
  287. 5, // 3
  288. 1, // 4
  289. 4, // 5
  290. 0, // 6
  291. 7 // 7
  292. };
  293. const int JPSNaturalStraightNeighbours = 1 << 4;
  294. const int JPSNaturalDiagonalNeighbours = (1 << 5) | (1 << 4) | (1 << 3);
  295. /// <summary>Memoization of what results to return from the Jump method.</summary>
  296. GridNodeBase[] JPSCache;
  297. /// <summary>
  298. /// Each byte is a bitfield where each bit indicates if direction number i should return null from the Jump method.
  299. /// Used as a cache.
  300. /// I would like to make this a ulong instead, but that could run into data races.
  301. /// </summary>
  302. byte[] JPSDead;
  303. ushort[] JPSLastCacheID;
  304. /// <summary>
  305. /// Executes a straight jump search.
  306. /// See: http://en.wikipedia.org/wiki/Jump_point_search
  307. /// </summary>
  308. static GridNodeBase JPSJumpStraight (GridNode node, Path path, PathHandler handler, int parentDir, int depth = 0) {
  309. GridGraph gg = GetGridGraph(node.GraphIndex);
  310. int[] neighbourOffsets = gg.neighbourOffsets;
  311. var nodes = gg.nodes;
  312. var origin = node;
  313. // Indexing into the cache arrays from multiple threads like this should cause
  314. // a lot of false sharing and cache trashing, but after profiling it seems
  315. // that this is not a major concern
  316. int threadID = handler.threadID;
  317. int threadOffset = 8*handler.threadID;
  318. int cyclicParentDir = JPSCyclic[parentDir];
  319. GridNodeBase result = null;
  320. // Rotate 180 degrees
  321. const int forwardDir = 4;
  322. int forwardOffset = neighbourOffsets[JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]];
  323. // Move forwards in the same direction
  324. // until a node is encountered which we either
  325. // * know the result for (memoization)
  326. // * is a special node (flag2 set)
  327. // * has custom connections
  328. // * the node has a forced neighbour
  329. // Then break out of the loop
  330. // and start another loop which goes through the same nodes and sets the
  331. // memoization caches to avoid expensive calls in the future
  332. while (true) {
  333. // This is needed to make sure different threads don't overwrite each others results
  334. // It doesn't matter if we throw away some caching done by other threads as this will only
  335. // happen during the first few path requests
  336. if (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount) {
  337. lock (node) {
  338. // Check again in case another thread has already created the array
  339. if (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount) {
  340. node.JPSCache = new GridNode[8*handler.totalThreadCount];
  341. node.JPSDead = new byte[handler.totalThreadCount];
  342. node.JPSLastCacheID = new ushort[handler.totalThreadCount];
  343. }
  344. }
  345. }
  346. if (node.JPSLastCacheID[threadID] != path.pathID) {
  347. for (int i = 0; i < 8; i++) node.JPSCache[i + threadOffset] = null;
  348. node.JPSLastCacheID[threadID] = path.pathID;
  349. node.JPSDead[threadID] = 0;
  350. }
  351. // Cache earlier results, major optimization
  352. // It is important to read from it once and then return the same result,
  353. // if we read from it twice, we might get different results due to other threads clearing the array sometimes
  354. var cachedResult = node.JPSCache[parentDir + threadOffset];
  355. if (cachedResult != null) {
  356. result = cachedResult;
  357. break;
  358. }
  359. if (((node.JPSDead[threadID] >> parentDir)&1) != 0) return null;
  360. // Special node (e.g end node), take care of
  361. if (handler.GetPathNode(node).flag2) {
  362. //Debug.Log ("Found end Node!");
  363. //Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green);
  364. result = node;
  365. break;
  366. }
  367. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  368. // Special node which has custom connections, take care of
  369. if (node.connections != null && node.connections.Length > 0) {
  370. result = node;
  371. break;
  372. }
  373. #endif
  374. // These are the nodes this node is connected to, one bit for each of the 8 directions
  375. int noncyclic = node.gridFlags;//We don't actually need to & with this because we don't use the other bits. & 0xFF;
  376. int cyclic = 0;
  377. for (int i = 0; i < 8; i++) cyclic |= ((noncyclic >> i)&0x1) << JPSCyclic[i];
  378. int forced = 0;
  379. // Loop around to be able to assume -X is where we came from
  380. cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;
  381. //for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForced[i];
  382. if ((cyclic & (1 << 2)) == 0) forced |= (1<<3);
  383. if ((cyclic & (1 << 6)) == 0) forced |= (1<<5);
  384. int natural = JPSNaturalStraightNeighbours;
  385. // Check if there are any forced neighbours which we can reach that are not natural neighbours
  386. //if ( ((forced & cyclic) & (~(natural & cyclic))) != 0 ) {
  387. if ((forced & (~natural) & cyclic) != 0) {
  388. // Some of the neighbour nodes are forced
  389. result = node;
  390. break;
  391. }
  392. // Make sure we can reach the next node
  393. if ((cyclic & (1 << forwardDir)) != 0) {
  394. node = nodes[node.nodeInGridIndex + forwardOffset] as GridNode;
  395. //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta);
  396. } else {
  397. result = null;
  398. break;
  399. }
  400. }
  401. if (result == null) {
  402. while (origin != node) {
  403. origin.JPSDead[threadID] |= (byte)(1 << parentDir);
  404. origin = nodes[origin.nodeInGridIndex + forwardOffset] as GridNode;
  405. }
  406. } else {
  407. while (origin != node) {
  408. origin.JPSCache[parentDir + threadOffset] = result;
  409. origin = nodes[origin.nodeInGridIndex + forwardOffset] as GridNode;
  410. }
  411. }
  412. return result;
  413. }
  414. /// <summary>
  415. /// Executes a diagonal jump search.
  416. /// See: http://en.wikipedia.org/wiki/Jump_point_search
  417. /// </summary>
  418. GridNodeBase JPSJumpDiagonal (Path path, PathHandler handler, int parentDir, int depth = 0) {
  419. // Indexing into the cache arrays from multiple threads like this should cause
  420. // a lot of false sharing and cache trashing, but after profiling it seems
  421. // that this is not a major concern
  422. int threadID = handler.threadID;
  423. int threadOffset = 8*handler.threadID;
  424. // This is needed to make sure different threads don't overwrite each others results
  425. // It doesn't matter if we throw away some caching done by other threads as this will only
  426. // happen during the first few path requests
  427. if (JPSLastCacheID == null || JPSLastCacheID.Length < handler.totalThreadCount) {
  428. lock (this) {
  429. // Check again in case another thread has already created the array
  430. if (JPSLastCacheID == null || JPSLastCacheID.Length < handler.totalThreadCount) {
  431. JPSCache = new GridNode[8*handler.totalThreadCount];
  432. JPSDead = new byte[handler.totalThreadCount];
  433. JPSLastCacheID = new ushort[handler.totalThreadCount];
  434. }
  435. }
  436. }
  437. if (JPSLastCacheID[threadID] != path.pathID) {
  438. for (int i = 0; i < 8; i++) JPSCache[i + threadOffset] = null;
  439. JPSLastCacheID[threadID] = path.pathID;
  440. JPSDead[threadID] = 0;
  441. }
  442. // Cache earlier results, major optimization
  443. // It is important to read from it once and then return the same result,
  444. // if we read from it twice, we might get different results due to other threads clearing the array sometimes
  445. var cachedResult = JPSCache[parentDir + threadOffset];
  446. if (cachedResult != null) {
  447. //return cachedResult;
  448. }
  449. //if ( ((JPSDead[threadID] >> parentDir)&1) != 0 ) return null;
  450. // Special node (e.g end node), take care of
  451. if (handler.GetPathNode(this).flag2) {
  452. //Debug.Log ("Found end Node!");
  453. //Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green);
  454. JPSCache[parentDir + threadOffset] = this;
  455. return this;
  456. }
  457. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  458. // Special node which has custom connections, take care of
  459. if (connections != null && connections.Length > 0) {
  460. JPSCache[parentDir] = this;
  461. return this;
  462. }
  463. #endif
  464. int noncyclic = gridFlags;//We don't actually need to & with this because we don't use the other bits. & 0xFF;
  465. int cyclic = 0;
  466. for (int i = 0; i < 8; i++) cyclic |= ((noncyclic >> i)&0x1) << JPSCyclic[i];
  467. int forced = 0;
  468. int cyclicParentDir = JPSCyclic[parentDir];
  469. // Loop around to be able to assume -X is where we came from
  470. cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;
  471. int natural;
  472. for (int i = 0; i < 8; i++) if (((cyclic >> i)&1) == 0) forced |= JPSForcedDiagonal[i];
  473. natural = JPSNaturalDiagonalNeighbours;
  474. /*
  475. * if ( ((Vector3)position - new Vector3(1.5f,0,-1.5f)).magnitude < 0.5f ) {
  476. * Debug.Log (noncyclic + " " + parentDir + " " + cyclicParentDir);
  477. * Debug.Log (System.Convert.ToString (cyclic, 2)+"\n"+System.Convert.ToString (noncyclic, 2)+"\n"+System.Convert.ToString (natural, 2)+"\n"+System.Convert.ToString (forced, 2));
  478. * }*/
  479. // Don't force nodes we cannot reach anyway
  480. forced &= cyclic;
  481. natural &= cyclic;
  482. if ((forced & (~natural)) != 0) {
  483. // Some of the neighbour nodes are forced
  484. JPSCache[parentDir+threadOffset] = this;
  485. return this;
  486. }
  487. int forwardDir;
  488. GridGraph gg = GetGridGraph(GraphIndex);
  489. int[] neighbourOffsets = gg.neighbourOffsets;
  490. var nodes = gg.nodes;
  491. {
  492. // Rotate 180 degrees - 1 node
  493. forwardDir = 3;
  494. if (((cyclic >> forwardDir)&1) != 0) {
  495. int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8];
  496. var other = nodes[nodeInGridIndex + neighbourOffsets[oi]];
  497. //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.black);
  498. GridNodeBase v;
  499. if (oi < 4) {
  500. v = JPSJumpStraight(other as GridNode, path, handler, JPSInverseCyclic[(cyclicParentDir-1+8)%8], depth+1);
  501. } else {
  502. v = (other as GridNode).JPSJumpDiagonal(path, handler, JPSInverseCyclic[(cyclicParentDir-1+8)%8], depth+1);
  503. }
  504. if (v != null) {
  505. JPSCache[parentDir+threadOffset] = this;
  506. return this;
  507. }
  508. }
  509. // Rotate 180 degrees + 1 node
  510. forwardDir = 5;
  511. if (((cyclic >> forwardDir)&1) != 0) {
  512. int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8];
  513. var other = nodes[nodeInGridIndex + neighbourOffsets[oi]];
  514. //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.grey);
  515. GridNodeBase v;
  516. if (oi < 4) {
  517. v = JPSJumpStraight(other as GridNode, path, handler, JPSInverseCyclic[(cyclicParentDir+1+8)%8], depth+1);
  518. } else {
  519. v = (other as GridNode).JPSJumpDiagonal(path, handler, JPSInverseCyclic[(cyclicParentDir+1+8)%8], depth+1);
  520. }
  521. if (v != null) {
  522. JPSCache[parentDir+threadOffset] = this;
  523. return this;
  524. }
  525. }
  526. }
  527. // Rotate 180 degrees
  528. forwardDir = 4;
  529. if (((cyclic >> forwardDir)&1) != 0) {
  530. int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8];
  531. var other = nodes[nodeInGridIndex + neighbourOffsets[oi]] as GridNode;
  532. //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta);
  533. var v = other.JPSJumpDiagonal(path, handler, parentDir, depth+1);
  534. if (v != null) {
  535. JPSCache[parentDir+threadOffset] = v;
  536. return v;
  537. }
  538. }
  539. JPSDead[threadID] |= (byte)(1 << parentDir);
  540. return null;
  541. }
  542. /// <summary>
  543. /// Opens a node using Jump Point Search.
  544. /// See: http://en.wikipedia.org/wiki/Jump_point_search
  545. /// </summary>
  546. public void JPSOpen (Path path, PathNode pathNode, PathHandler handler) {
  547. GridGraph gg = GetGridGraph(GraphIndex);
  548. int[] neighbourOffsets = gg.neighbourOffsets;
  549. var nodes = gg.nodes;
  550. ushort pid = handler.PathID;
  551. int noncyclic = gridFlags & 0xFF;
  552. int cyclic = 0;
  553. for (int i = 0; i < 8; i++) cyclic |= ((noncyclic >> i)&0x1) << JPSCyclic[i];
  554. var parent = pathNode.parent != null ? pathNode.parent.node as GridNode : null;
  555. int parentDir = -1;
  556. if (parent != null) {
  557. int diff = parent != null ? parent.nodeInGridIndex - nodeInGridIndex : 0;
  558. int x2 = nodeInGridIndex % gg.width;
  559. int x1 = parent.nodeInGridIndex % gg.width;
  560. if (diff < 0) {
  561. if (x1 == x2) {
  562. parentDir = 0;
  563. } else if (x1 < x2) {
  564. parentDir = 7;
  565. } else {
  566. parentDir = 4;
  567. }
  568. } else {
  569. if (x1 == x2) {
  570. parentDir = 1;
  571. } else if (x1 < x2) {
  572. parentDir = 6;
  573. } else {
  574. parentDir = 5;
  575. }
  576. }
  577. }
  578. int cyclicParentDir = 0;
  579. // Check for -1
  580. int forced = 0;
  581. if (parentDir != -1) {
  582. cyclicParentDir = JPSCyclic[parentDir];
  583. // Loop around to be able to assume -X is where we came from
  584. cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;
  585. } else {
  586. forced = 0xFF;
  587. //parentDir = 0;
  588. }
  589. bool diagonal = parentDir >= 4;
  590. int natural;
  591. if (diagonal) {
  592. for (int i = 0; i < 8; i++) if (((cyclic >> i)&1) == 0) forced |= JPSForcedDiagonal[i];
  593. natural = JPSNaturalDiagonalNeighbours;
  594. } else {
  595. for (int i = 0; i < 8; i++) if (((cyclic >> i)&1) == 0) forced |= JPSForced[i];
  596. natural = JPSNaturalStraightNeighbours;
  597. }
  598. // Don't force nodes we cannot reach anyway
  599. forced &= cyclic;
  600. natural &= cyclic;
  601. int nb = forced | natural;
  602. /*if ( ((Vector3)position - new Vector3(0.5f,0,3.5f)).magnitude < 0.5f ) {
  603. * Debug.Log (noncyclic + " " + parentDir + " " + cyclicParentDir);
  604. * Debug.Log (System.Convert.ToString (cyclic, 2)+"\n"+System.Convert.ToString (noncyclic, 2)+"\n"+System.Convert.ToString (natural, 2)+"\n"+System.Convert.ToString (forced, 2));
  605. * }*/
  606. for (int i = 0; i < 8; i++) {
  607. if (((nb >> i)&1) != 0) {
  608. int oi = JPSInverseCyclic[(i + cyclicParentDir) % 8];
  609. var other = nodes[nodeInGridIndex + neighbourOffsets[oi]];
  610. #if ASTARDEBUG
  611. if (((forced >> i)&1) != 0) {
  612. Debug.DrawLine((Vector3)position, Vector3.Lerp((Vector3)other.position, (Vector3)position, 0.6f), Color.red);
  613. }
  614. if (((natural >> i)&1) != 0) {
  615. Debug.DrawLine((Vector3)position + Vector3.up*0.2f, Vector3.Lerp((Vector3)other.position, (Vector3)position, 0.6f) + Vector3.up*0.2f, Color.green);
  616. }
  617. #endif
  618. if (oi < 4) {
  619. other = JPSJumpStraight(other as GridNode, path, handler, JPSInverseCyclic[(i + 4 + cyclicParentDir) % 8]);
  620. } else {
  621. other = (other as GridNode).JPSJumpDiagonal(path, handler, JPSInverseCyclic[(i + 4 + cyclicParentDir) % 8]);
  622. }
  623. if (other != null) {
  624. //Debug.DrawLine ( (Vector3)position + Vector3.up*0.0f, (Vector3)other.position + Vector3.up*0.3f, Color.cyan);
  625. //Debug.DrawRay ( (Vector3)other.position, Vector3.up, Color.cyan);
  626. //GridNode other = nodes[nodeInGridIndex + neighbourOffsets[i]];
  627. //if (!path.CanTraverse (other)) continue;
  628. PathNode otherPN = handler.GetPathNode(other);
  629. if (otherPN.pathID != pid) {
  630. otherPN.parent = pathNode;
  631. otherPN.pathID = pid;
  632. otherPN.cost = (uint)(other.position - position).costMagnitude;//neighbourCosts[i];
  633. otherPN.H = path.CalculateHScore(other);
  634. otherPN.UpdateG(path);
  635. //Debug.Log ("G " + otherPN.G + " F " + otherPN.F);
  636. handler.heap.Add(otherPN);
  637. //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue);
  638. } else {
  639. //If not we can test if the path from the current node to this one is a better one then the one already used
  640. uint tmpCost = (uint)(other.position - position).costMagnitude;//neighbourCosts[i];
  641. if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G) {
  642. //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
  643. otherPN.cost = tmpCost;
  644. otherPN.parent = pathNode;
  645. other.UpdateRecursiveG(path, otherPN, handler);
  646. }
  647. }
  648. }
  649. }
  650. #if ASTARDEBUG
  651. if (i == 0 && parentDir != -1 && this.nodeInGridIndex > 10) {
  652. int oi = JPSInverseCyclic[(i + cyclicParentDir) % 8];
  653. if (nodeInGridIndex + neighbourOffsets[oi] < 0 || nodeInGridIndex + neighbourOffsets[oi] >= nodes.Length) {
  654. //Debug.LogError ("ERR: " + (nodeInGridIndex + neighbourOffsets[oi]) + " " + cyclicParentDir + " " + parentDir + " Reverted " + oi);
  655. //Debug.DrawRay ((Vector3)position, Vector3.up, Color.red);
  656. } else {
  657. var other = nodes[nodeInGridIndex + neighbourOffsets[oi]];
  658. Debug.DrawLine((Vector3)position - Vector3.up*0.2f, Vector3.Lerp((Vector3)other.position, (Vector3)position, 0.6f) - Vector3.up*0.2f, Color.blue);
  659. }
  660. }
  661. #endif
  662. }
  663. }
  664. #endif
  665. public override void Open (Path path, PathNode pathNode, PathHandler handler) {
  666. GridGraph gg = GetGridGraph(GraphIndex);
  667. ushort pid = handler.PathID;
  668. #if ASTAR_JPS
  669. if (gg.useJumpPointSearch && !path.FloodingPath) {
  670. JPSOpen(path, pathNode, handler);
  671. } else
  672. #endif
  673. {
  674. int[] neighbourOffsets = gg.neighbourOffsets;
  675. uint[] neighbourCosts = gg.neighbourCosts;
  676. GridNodeBase[] nodes = gg.nodes;
  677. var index = NodeInGridIndex;
  678. for (int i = 0; i < 8; i++) {
  679. if (HasConnectionInDirection(i)) {
  680. GridNodeBase other = nodes[index + neighbourOffsets[i]];
  681. if (!path.CanTraverse(other)) continue;
  682. PathNode otherPN = handler.GetPathNode(other);
  683. uint tmpCost = neighbourCosts[i];
  684. // Check if the other node has not yet been visited by this path
  685. if (otherPN.pathID != pid) {
  686. otherPN.parent = pathNode;
  687. otherPN.pathID = pid;
  688. otherPN.cost = tmpCost;
  689. otherPN.H = path.CalculateHScore(other);
  690. otherPN.UpdateG(path);
  691. handler.heap.Add(otherPN);
  692. } else {
  693. // Sorry for the huge number of #ifs
  694. //If not we can test if the path from the current node to this one is a better one then the one already used
  695. #if ASTAR_NO_TRAVERSAL_COST
  696. if (pathNode.G+tmpCost < otherPN.G)
  697. #else
  698. if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G)
  699. #endif
  700. {
  701. //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
  702. otherPN.cost = tmpCost;
  703. otherPN.parent = pathNode;
  704. other.UpdateRecursiveG(path, otherPN, handler);
  705. }
  706. }
  707. }
  708. }
  709. }
  710. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  711. base.Open(path, pathNode, handler);
  712. #endif
  713. }
  714. public override void SerializeNode (GraphSerializationContext ctx) {
  715. base.SerializeNode(ctx);
  716. ctx.SerializeInt3(position);
  717. ctx.writer.Write(gridFlags);
  718. }
  719. public override void DeserializeNode (GraphSerializationContext ctx) {
  720. base.DeserializeNode(ctx);
  721. position = ctx.DeserializeInt3();
  722. gridFlags = ctx.reader.ReadUInt16();
  723. }
  724. public override void AddConnection (GraphNode node, uint cost) {
  725. // In case the node was already added as an internal grid connection,
  726. // we need to remove that connection before we insert it as a custom connection.
  727. // Using a custom connection is necessary because it has a custom cost.
  728. if (node is GridNode gn && gn.GraphIndex == GraphIndex) {
  729. RemoveGridConnection(gn);
  730. }
  731. base.AddConnection(node, cost);
  732. }
  733. public override void RemoveConnection (GraphNode node) {
  734. base.RemoveConnection(node);
  735. // If the node is a grid node on the same graph, it might be added as an internal connection and not a custom one.
  736. if (node is GridNode gn && gn.GraphIndex == GraphIndex) {
  737. RemoveGridConnection(gn);
  738. }
  739. }
  740. /// <summary>
  741. /// Removes a connection from the internal grid connections.
  742. /// See: SetConnectionInternal
  743. /// </summary>
  744. protected void RemoveGridConnection (GridNode node) {
  745. var nodeIndex = NodeInGridIndex;
  746. var gg = GetGridGraph(GraphIndex);
  747. for (int i = 0; i < 8; i++) {
  748. if (nodeIndex + gg.neighbourOffsets[i] == node.NodeInGridIndex && GetNeighbourAlongDirection(i) == node) {
  749. SetConnectionInternal(i, false);
  750. break;
  751. }
  752. }
  753. }
  754. #else
  755. public override void AddConnection (GraphNode node, uint cost) {
  756. throw new System.NotImplementedException();
  757. }
  758. public override void ClearConnections (bool alsoReverse) {
  759. throw new System.NotImplementedException();
  760. }
  761. public override void GetConnections (GraphNodeDelegate del) {
  762. throw new System.NotImplementedException();
  763. }
  764. public override void Open (Path path, PathNode pathNode, PathHandler handler) {
  765. throw new System.NotImplementedException();
  766. }
  767. public override void RemoveConnection (GraphNode node) {
  768. throw new System.NotImplementedException();
  769. }
  770. #endif
  771. }
  772. }