GridNodeBase.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. using UnityEngine;
  2. using Pathfinding.Serialization;
  3. namespace Pathfinding {
  4. /// <summary>Base class for GridNode and LevelGridNode</summary>
  5. public abstract class GridNodeBase : GraphNode {
  6. protected GridNodeBase (AstarPath astar) : base(astar) {
  7. }
  8. const int GridFlagsWalkableErosionOffset = 8;
  9. const int GridFlagsWalkableErosionMask = 1 << GridFlagsWalkableErosionOffset;
  10. const int GridFlagsWalkableTmpOffset = 9;
  11. const int GridFlagsWalkableTmpMask = 1 << GridFlagsWalkableTmpOffset;
  12. protected const int NodeInGridIndexLayerOffset = 24;
  13. protected const int NodeInGridIndexMask = 0xFFFFFF;
  14. /// <summary>
  15. /// Bitfield containing the x and z coordinates of the node as well as the layer (for layered grid graphs).
  16. /// See: NodeInGridIndex
  17. /// </summary>
  18. protected int nodeInGridIndex;
  19. protected ushort gridFlags;
  20. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  21. /// <summary>
  22. /// Custon non-grid connections from this node.
  23. /// See: <see cref="AddConnection"/>
  24. /// See: <see cref="RemoveConnection"/>
  25. ///
  26. /// This field is removed if the ASTAR_GRID_NO_CUSTOM_CONNECTIONS compiler directive is used.
  27. /// Removing it can save a tiny bit of memory. You can enable the define in the Optimizations tab in the A* inspector.
  28. /// See: compiler-directives (view in online documentation for working links)
  29. ///
  30. /// Note: If you modify this array or the contents of it you must call <see cref="SetConnectivityDirty"/>.
  31. /// </summary>
  32. public Connection[] connections;
  33. #endif
  34. /// <summary>
  35. /// The index of the node in the grid.
  36. /// This is x + z*graph.width
  37. /// So you can get the X and Z indices using
  38. /// <code>
  39. /// int index = node.NodeInGridIndex;
  40. /// int x = index % graph.width;
  41. /// int z = index / graph.width;
  42. /// // where graph is GridNode.GetGridGraph (node.graphIndex), i.e the graph the nodes are contained in.
  43. /// </code>
  44. /// </summary>
  45. public int NodeInGridIndex { get { return nodeInGridIndex & NodeInGridIndexMask; } set { nodeInGridIndex = (nodeInGridIndex & ~NodeInGridIndexMask) | value; } }
  46. /// <summary>
  47. /// X coordinate of the node in the grid.
  48. /// The node in the bottom left corner has (x,z) = (0,0) and the one in the opposite
  49. /// corner has (x,z) = (width-1, depth-1)
  50. /// See: ZCoordInGrid
  51. /// See: NodeInGridIndex
  52. /// </summary>
  53. public int XCoordinateInGrid {
  54. get {
  55. return NodeInGridIndex % GridNode.GetGridGraph(GraphIndex).width;
  56. }
  57. }
  58. /// <summary>
  59. /// Z coordinate of the node in the grid.
  60. /// The node in the bottom left corner has (x,z) = (0,0) and the one in the opposite
  61. /// corner has (x,z) = (width-1, depth-1)
  62. /// See: XCoordInGrid
  63. /// See: NodeInGridIndex
  64. /// </summary>
  65. public int ZCoordinateInGrid {
  66. get {
  67. return NodeInGridIndex / GridNode.GetGridGraph(GraphIndex).width;
  68. }
  69. }
  70. /// <summary>
  71. /// Stores walkability before erosion is applied.
  72. /// Used internally when updating the graph.
  73. /// </summary>
  74. public bool WalkableErosion {
  75. get {
  76. return (gridFlags & GridFlagsWalkableErosionMask) != 0;
  77. }
  78. set {
  79. unchecked { gridFlags = (ushort)(gridFlags & ~GridFlagsWalkableErosionMask | (value ? (ushort)GridFlagsWalkableErosionMask : (ushort)0)); }
  80. }
  81. }
  82. /// <summary>Temporary variable used internally when updating the graph.</summary>
  83. public bool TmpWalkable {
  84. get {
  85. return (gridFlags & GridFlagsWalkableTmpMask) != 0;
  86. }
  87. set {
  88. unchecked { gridFlags = (ushort)(gridFlags & ~GridFlagsWalkableTmpMask | (value ? (ushort)GridFlagsWalkableTmpMask : (ushort)0)); }
  89. }
  90. }
  91. /// <summary>
  92. /// True if the node has grid connections to all its 8 neighbours.
  93. /// Note: This will always return false if GridGraph.neighbours is set to anything other than Eight.
  94. /// See: GetNeighbourAlongDirection
  95. /// </summary>
  96. public abstract bool HasConnectionsToAllEightNeighbours { get; }
  97. public override float SurfaceArea () {
  98. GridGraph gg = GridNode.GetGridGraph(GraphIndex);
  99. return gg.nodeSize*gg.nodeSize;
  100. }
  101. public override Vector3 RandomPointOnSurface () {
  102. GridGraph gg = GridNode.GetGridGraph(GraphIndex);
  103. var graphSpacePosition = gg.transform.InverseTransform((Vector3)position);
  104. return gg.transform.Transform(graphSpacePosition + new Vector3(Random.value - 0.5f, 0, Random.value - 0.5f));
  105. }
  106. /// <summary>
  107. /// Transforms a world space point to a normalized point on this node's surface.
  108. /// (0.5,0.5) represents the node's center. (0,0), (1,0), (1,1) and (0,1) each represent the corners of the node.
  109. ///
  110. /// See: <see cref="UnNormalizePoint"/>
  111. /// </summary>
  112. public Vector2 NormalizePoint (Vector3 worldPoint) {
  113. GridGraph gg = GridNode.GetGridGraph(GraphIndex);
  114. var graphSpacePosition = gg.transform.InverseTransform(worldPoint);
  115. return new Vector2(graphSpacePosition.x - this.XCoordinateInGrid, graphSpacePosition.z - this.ZCoordinateInGrid);
  116. }
  117. /// <summary>
  118. /// Transforms a normalized point on this node's surface to a world space point.
  119. /// (0.5,0.5) represents the node's center. (0,0), (1,0), (1,1) and (0,1) each represent the corners of the node.
  120. ///
  121. /// See: <see cref="NormalizePoint"/>
  122. /// </summary>
  123. public Vector3 UnNormalizePoint (Vector2 normalizedPointOnSurface) {
  124. GridGraph gg = GridNode.GetGridGraph(GraphIndex);
  125. return (Vector3)this.position + gg.transform.TransformVector(new Vector3(normalizedPointOnSurface.x - 0.5f, 0, normalizedPointOnSurface.y - 0.5f));
  126. }
  127. public override int GetGizmoHashCode () {
  128. var hash = base.GetGizmoHashCode();
  129. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  130. if (connections != null) {
  131. for (int i = 0; i < connections.Length; i++) {
  132. hash ^= 17 * connections[i].GetHashCode();
  133. }
  134. }
  135. #endif
  136. hash ^= 109 * gridFlags;
  137. return hash;
  138. }
  139. /// <summary>
  140. /// Adjacent grid node in the specified direction.
  141. /// This will return null if the node does not have a connection to a node
  142. /// in that direction.
  143. ///
  144. /// The dir parameter corresponds to directions in the grid as:
  145. /// <code>
  146. /// Z
  147. /// |
  148. /// |
  149. ///
  150. /// 6 2 5
  151. /// \ | /
  152. /// -- 3 - X - 1 ----- X
  153. /// / | \
  154. /// 7 0 4
  155. ///
  156. /// |
  157. /// |
  158. /// </code>
  159. ///
  160. /// See: GetConnections
  161. ///
  162. /// Note: This method only takes grid connections into account, not custom connections (i.e. those added using <see cref="AddConnection"/> or using node links).
  163. /// </summary>
  164. public abstract GridNodeBase GetNeighbourAlongDirection(int direction);
  165. /// <summary>
  166. /// True if the node has a connection to an adjecent node in the specified direction.
  167. ///
  168. /// The dir parameter corresponds to directions in the grid as:
  169. /// <code>
  170. /// Z
  171. /// |
  172. /// |
  173. ///
  174. /// 6 2 5
  175. /// \ | /
  176. /// -- 3 - X - 1 ----- X
  177. /// / | \
  178. /// 7 0 4
  179. ///
  180. /// |
  181. /// |
  182. /// </code>
  183. ///
  184. /// See: <see cref="GetConnections"/>
  185. /// See: <see cref="GetNeighbourAlongDirection"/>
  186. ///
  187. /// Note: This method only takes grid connections into account, not custom connections (i.e. those added using <see cref="AddConnection"/> or using node links).
  188. /// </summary>
  189. public virtual bool HasConnectionInDirection (int direction) {
  190. // TODO: Can be optimized if overriden in each subclass
  191. return GetNeighbourAlongDirection(direction) != null;
  192. }
  193. public override bool ContainsConnection (GraphNode node) {
  194. #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  195. if (connections != null) {
  196. for (int i = 0; i < connections.Length; i++) {
  197. if (connections[i].node == node) {
  198. return true;
  199. }
  200. }
  201. }
  202. #endif
  203. for (int i = 0; i < 8; i++) {
  204. if (node == GetNeighbourAlongDirection(i)) {
  205. return true;
  206. }
  207. }
  208. return false;
  209. }
  210. #if ASTAR_GRID_NO_CUSTOM_CONNECTIONS
  211. public override void AddConnection (GraphNode node, uint cost) {
  212. throw new System.NotImplementedException("GridNodes do not have support for adding manual connections with your current settings."+
  213. "\nPlease disable ASTAR_GRID_NO_CUSTOM_CONNECTIONS in the Optimizations tab in the A* Inspector");
  214. }
  215. public override void RemoveConnection (GraphNode node) {
  216. throw new System.NotImplementedException("GridNodes do not have support for adding manual connections with your current settings."+
  217. "\nPlease disable ASTAR_GRID_NO_CUSTOM_CONNECTIONS in the Optimizations tab in the A* Inspector");
  218. }
  219. public void ClearCustomConnections (bool alsoReverse) {
  220. }
  221. #else
  222. /// <summary>Same as <see cref="ClearConnections"/>, but does not clear grid connections, only custom ones (e.g added by <see cref="AddConnection"/> or a NodeLink component)</summary>
  223. public void ClearCustomConnections (bool alsoReverse) {
  224. if (connections != null) for (int i = 0; i < connections.Length; i++) connections[i].node.RemoveConnection(this);
  225. connections = null;
  226. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  227. }
  228. public override void ClearConnections (bool alsoReverse) {
  229. ClearCustomConnections(alsoReverse);
  230. }
  231. public override void GetConnections (System.Action<GraphNode> action) {
  232. if (connections != null) for (int i = 0; i < connections.Length; i++) action(connections[i].node);
  233. }
  234. public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
  235. ushort pid = handler.PathID;
  236. if (connections != null) for (int i = 0; i < connections.Length; i++) {
  237. GraphNode other = connections[i].node;
  238. PathNode otherPN = handler.GetPathNode(other);
  239. if (otherPN.parent == pathNode && otherPN.pathID == pid) other.UpdateRecursiveG(path, otherPN, handler);
  240. }
  241. }
  242. public override void Open (Path path, PathNode pathNode, PathHandler handler) {
  243. ushort pid = handler.PathID;
  244. if (connections != null) for (int i = 0; i < connections.Length; i++) {
  245. GraphNode other = connections[i].node;
  246. if (!path.CanTraverse(other)) continue;
  247. PathNode otherPN = handler.GetPathNode(other);
  248. uint tmpCost = connections[i].cost;
  249. if (otherPN.pathID != pid) {
  250. otherPN.parent = pathNode;
  251. otherPN.pathID = pid;
  252. otherPN.cost = tmpCost;
  253. otherPN.H = path.CalculateHScore(other);
  254. otherPN.UpdateG(path);
  255. //Debug.Log ("G " + otherPN.G + " F " + otherPN.F);
  256. handler.heap.Add(otherPN);
  257. //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue);
  258. } else {
  259. // Sorry for the huge number of #ifs
  260. //If not we can test if the path from the current node to this one is a better one then the one already used
  261. #if ASTAR_NO_TRAVERSAL_COST
  262. if (pathNode.G+tmpCost < otherPN.G)
  263. #else
  264. if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G)
  265. #endif
  266. {
  267. //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
  268. otherPN.cost = tmpCost;
  269. otherPN.parent = pathNode;
  270. other.UpdateRecursiveG(path, otherPN, handler);
  271. }
  272. }
  273. }
  274. }
  275. /// <summary>
  276. /// Add a connection from this node to the specified node.
  277. /// If the connection already exists, the cost will simply be updated and
  278. /// no extra connection added.
  279. ///
  280. /// Note: Only adds a one-way connection. Consider calling the same function on the other node
  281. /// to get a two-way connection.
  282. ///
  283. /// Note that this will always add a custom connection which is a bit slower than the internal connection
  284. /// list to adjacent grid nodes. If you only want to modify connections between grid nodes that are
  285. /// adjacent to each other, and don't want custom costs, then using <see cref="SetConnectionInternal"/> might be
  286. /// a better option.
  287. /// </summary>
  288. public override void AddConnection (GraphNode node, uint cost) {
  289. if (node == null) throw new System.ArgumentNullException();
  290. if (connections != null) {
  291. for (int i = 0; i < connections.Length; i++) {
  292. if (connections[i].node == node) {
  293. connections[i].cost = cost;
  294. return;
  295. }
  296. }
  297. }
  298. int connLength = connections != null ? connections.Length : 0;
  299. var newconns = new Connection[connLength+1];
  300. for (int i = 0; i < connLength; i++) {
  301. newconns[i] = connections[i];
  302. }
  303. newconns[connLength] = new Connection(node, cost);
  304. connections = newconns;
  305. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  306. }
  307. /// <summary>
  308. /// Removes any connection from this node to the specified node.
  309. /// If no such connection exists, nothing will be done.
  310. ///
  311. /// Note: This only removes the connection from this node to the other node.
  312. /// You may want to call the same function on the other node to remove its eventual connection
  313. /// to this node.
  314. ///
  315. /// Version: Before 4.3.48 This method only handled custom connections (those added using link components or the AddConnection method).
  316. /// Regular grid connections had to be added or removed using <see cref="Pathfinding.GridNode.SetConnectionInternal"/>. Starting with 4.3.48 this method
  317. /// can remove all types of connections.
  318. /// </summary>
  319. public override void RemoveConnection (GraphNode node) {
  320. if (connections == null) return;
  321. for (int i = 0; i < connections.Length; i++) {
  322. if (connections[i].node == node) {
  323. int connLength = connections.Length;
  324. var newconns = new Connection[connLength-1];
  325. for (int j = 0; j < i; j++) {
  326. newconns[j] = connections[j];
  327. }
  328. for (int j = i+1; j < connLength; j++) {
  329. newconns[j-1] = connections[j];
  330. }
  331. connections = newconns;
  332. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  333. return;
  334. }
  335. }
  336. }
  337. public override void SerializeReferences (GraphSerializationContext ctx) {
  338. // TODO: Deduplicate code
  339. if (connections == null) {
  340. ctx.writer.Write(-1);
  341. } else {
  342. ctx.writer.Write(connections.Length);
  343. for (int i = 0; i < connections.Length; i++) {
  344. ctx.SerializeNodeReference(connections[i].node);
  345. ctx.writer.Write(connections[i].cost);
  346. }
  347. }
  348. }
  349. public override void DeserializeReferences (GraphSerializationContext ctx) {
  350. // Grid nodes didn't serialize references before 3.8.3
  351. if (ctx.meta.version < AstarSerializer.V3_8_3)
  352. return;
  353. int count = ctx.reader.ReadInt32();
  354. if (count == -1) {
  355. connections = null;
  356. } else {
  357. connections = new Connection[count];
  358. for (int i = 0; i < count; i++) {
  359. connections[i] = new Connection(ctx.DeserializeNodeReference(), ctx.reader.ReadUInt32());
  360. }
  361. }
  362. }
  363. #endif
  364. }
  365. }