PointNode.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. using UnityEngine;
  2. using Pathfinding.Serialization;
  3. namespace Pathfinding {
  4. /// <summary>
  5. /// Node used for the PointGraph.
  6. /// This is just a simple point with a list of connections (and associated costs) to other nodes.
  7. /// It does not have any concept of a surface like many other node types.
  8. ///
  9. /// See: PointGraph
  10. /// </summary>
  11. public class PointNode : GraphNode {
  12. /// <summary>
  13. /// All connections from this node.
  14. /// See: <see cref="AddConnection"/>
  15. /// See: <see cref="RemoveConnection"/>
  16. ///
  17. /// Note: If you modify this array or the contents of it you must call <see cref="SetConnectivityDirty"/>.
  18. ///
  19. /// Note: If you modify this array or the contents of it you must call <see cref="PointGraph.RegisterConnectionLength"/> with the length of the new connections.
  20. /// </summary>
  21. public Connection[] connections;
  22. /// <summary>
  23. /// GameObject this node was created from (if any).
  24. /// Warning: When loading a graph from a saved file or from cache, this field will be null.
  25. ///
  26. /// <code>
  27. /// var node = AstarPath.active.GetNearest(transform.position).node;
  28. /// var pointNode = node as PointNode;
  29. ///
  30. /// if (pointNode != null) {
  31. /// Debug.Log("That node was created from the GameObject named " + pointNode.gameObject.name);
  32. /// } else {
  33. /// Debug.Log("That node is not a PointNode");
  34. /// }
  35. /// </code>
  36. /// </summary>
  37. public GameObject gameObject;
  38. public void SetPosition (Int3 value) {
  39. position = value;
  40. }
  41. public PointNode (AstarPath astar) : base(astar) {
  42. }
  43. /// <summary>
  44. /// Closest point on the surface of this node to the point p.
  45. ///
  46. /// For a point node this is always the node's <see cref="position"/> sicne it has no surface.
  47. /// </summary>
  48. public override Vector3 ClosestPointOnNode (Vector3 p) {
  49. return (Vector3)this.position;
  50. }
  51. public override void GetConnections (System.Action<GraphNode> action) {
  52. if (connections == null) return;
  53. for (int i = 0; i < connections.Length; i++) action(connections[i].node);
  54. }
  55. public override void ClearConnections (bool alsoReverse) {
  56. if (alsoReverse && connections != null) {
  57. for (int i = 0; i < connections.Length; i++) {
  58. connections[i].node.RemoveConnection(this);
  59. }
  60. }
  61. connections = null;
  62. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  63. }
  64. public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
  65. pathNode.UpdateG(path);
  66. handler.heap.Add(pathNode);
  67. for (int i = 0; i < connections.Length; i++) {
  68. GraphNode other = connections[i].node;
  69. PathNode otherPN = handler.GetPathNode(other);
  70. if (otherPN.parent == pathNode && otherPN.pathID == handler.PathID) {
  71. other.UpdateRecursiveG(path, otherPN, handler);
  72. }
  73. }
  74. }
  75. public override bool ContainsConnection (GraphNode node) {
  76. if (connections == null) return false;
  77. for (int i = 0; i < connections.Length; i++) if (connections[i].node == node) return true;
  78. return false;
  79. }
  80. /// <summary>
  81. /// Add a connection from this node to the specified node.
  82. /// If the connection already exists, the cost will simply be updated and
  83. /// no extra connection added.
  84. ///
  85. /// Note: Only adds a one-way connection. Consider calling the same function on the other node
  86. /// to get a two-way connection.
  87. ///
  88. /// <code>
  89. /// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => {
  90. /// // Connect two nodes
  91. /// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node;
  92. /// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node;
  93. /// var cost = (uint)(node2.position - node1.position).costMagnitude;
  94. /// node1.AddConnection(node2, cost);
  95. /// node2.AddConnection(node1, cost);
  96. ///
  97. /// node1.ContainsConnection(node2); // True
  98. ///
  99. /// node1.RemoveConnection(node2);
  100. /// node2.RemoveConnection(node1);
  101. /// }));
  102. /// </code>
  103. /// </summary>
  104. public override void AddConnection (GraphNode node, uint cost) {
  105. if (node == null) throw new System.ArgumentNullException();
  106. if (connections != null) {
  107. for (int i = 0; i < connections.Length; i++) {
  108. if (connections[i].node == node) {
  109. connections[i].cost = cost;
  110. return;
  111. }
  112. }
  113. }
  114. int connLength = connections != null ? connections.Length : 0;
  115. var newconns = new Connection[connLength+1];
  116. for (int i = 0; i < connLength; i++) {
  117. newconns[i] = connections[i];
  118. }
  119. newconns[connLength] = new Connection(node, cost);
  120. connections = newconns;
  121. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  122. // Make sure the graph knows that there exists a connection with this length
  123. (this.Graph as PointGraph).RegisterConnectionLength((node.position - position).sqrMagnitudeLong);
  124. }
  125. /// <summary>
  126. /// Removes any connection from this node to the specified node.
  127. /// If no such connection exists, nothing will be done.
  128. ///
  129. /// Note: This only removes the connection from this node to the other node.
  130. /// You may want to call the same function on the other node to remove its possible connection
  131. /// to this node.
  132. ///
  133. /// <code>
  134. /// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => {
  135. /// // Connect two nodes
  136. /// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node;
  137. /// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node;
  138. /// var cost = (uint)(node2.position - node1.position).costMagnitude;
  139. /// node1.AddConnection(node2, cost);
  140. /// node2.AddConnection(node1, cost);
  141. ///
  142. /// node1.ContainsConnection(node2); // True
  143. ///
  144. /// node1.RemoveConnection(node2);
  145. /// node2.RemoveConnection(node1);
  146. /// }));
  147. /// </code>
  148. /// </summary>
  149. public override void RemoveConnection (GraphNode node) {
  150. if (connections == null) return;
  151. for (int i = 0; i < connections.Length; i++) {
  152. if (connections[i].node == node) {
  153. int connLength = connections.Length;
  154. var newconns = new Connection[connLength-1];
  155. for (int j = 0; j < i; j++) {
  156. newconns[j] = connections[j];
  157. }
  158. for (int j = i+1; j < connLength; j++) {
  159. newconns[j-1] = connections[j];
  160. }
  161. connections = newconns;
  162. AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
  163. return;
  164. }
  165. }
  166. }
  167. public override void Open (Path path, PathNode pathNode, PathHandler handler) {
  168. if (connections == null) return;
  169. for (int i = 0; i < connections.Length; i++) {
  170. GraphNode other = connections[i].node;
  171. if (path.CanTraverse(other)) {
  172. PathNode pathOther = handler.GetPathNode(other);
  173. if (pathOther.pathID != handler.PathID) {
  174. pathOther.parent = pathNode;
  175. pathOther.pathID = handler.PathID;
  176. pathOther.cost = connections[i].cost;
  177. pathOther.H = path.CalculateHScore(other);
  178. pathOther.UpdateG(path);
  179. handler.heap.Add(pathOther);
  180. } else {
  181. //If not we can test if the path from this node to the other one is a better one then the one already used
  182. uint tmpCost = connections[i].cost;
  183. if (pathNode.G + tmpCost + path.GetTraversalCost(other) < pathOther.G) {
  184. pathOther.cost = tmpCost;
  185. pathOther.parent = pathNode;
  186. other.UpdateRecursiveG(path, pathOther, handler);
  187. }
  188. }
  189. }
  190. }
  191. }
  192. public override int GetGizmoHashCode () {
  193. var hash = base.GetGizmoHashCode();
  194. if (connections != null) {
  195. for (int i = 0; i < connections.Length; i++) {
  196. hash ^= 17 * connections[i].GetHashCode();
  197. }
  198. }
  199. return hash;
  200. }
  201. public override void SerializeNode (GraphSerializationContext ctx) {
  202. base.SerializeNode(ctx);
  203. ctx.SerializeInt3(position);
  204. }
  205. public override void DeserializeNode (GraphSerializationContext ctx) {
  206. base.DeserializeNode(ctx);
  207. position = ctx.DeserializeInt3();
  208. }
  209. public override void SerializeReferences (GraphSerializationContext ctx) {
  210. if (connections == null) {
  211. ctx.writer.Write(-1);
  212. } else {
  213. ctx.writer.Write(connections.Length);
  214. for (int i = 0; i < connections.Length; i++) {
  215. ctx.SerializeNodeReference(connections[i].node);
  216. ctx.writer.Write(connections[i].cost);
  217. }
  218. }
  219. }
  220. public override void DeserializeReferences (GraphSerializationContext ctx) {
  221. int count = ctx.reader.ReadInt32();
  222. if (count == -1) {
  223. connections = null;
  224. } else {
  225. connections = new Connection[count];
  226. for (int i = 0; i < count; i++) {
  227. connections[i] = new Connection(ctx.DeserializeNodeReference(), ctx.reader.ReadUInt32());
  228. }
  229. }
  230. }
  231. }
  232. }