using UnityEngine;
using Pathfinding.Serialization;
namespace Pathfinding {
///
/// Node used for the PointGraph.
/// This is just a simple point with a list of connections (and associated costs) to other nodes.
/// It does not have any concept of a surface like many other node types.
///
/// See: PointGraph
///
public class PointNode : GraphNode {
///
/// All connections from this node.
/// See:
/// See:
/// See:
///
/// Note: If you modify this array or the contents of it you must call .
///
/// Note: If you modify this array or the contents of it you must call with the length of the new connections.
///
/// This may be null if the node has no connections to other nodes.
///
public Connection[] connections;
///
/// GameObject this node was created from (if any).
/// Warning: When loading a graph from a saved file or from cache, this field will be null.
///
///
/// var node = AstarPath.active.GetNearest(transform.position).node;
/// var pointNode = node as PointNode;
///
/// if (pointNode != null) {
/// Debug.Log("That node was created from the GameObject named " + pointNode.gameObject.name);
/// } else {
/// Debug.Log("That node is not a PointNode");
/// }
///
///
public GameObject gameObject;
[System.Obsolete("Set node.position instead")]
public void SetPosition (Int3 value) {
position = value;
}
public PointNode() { }
public PointNode (AstarPath astar) {
astar.InitializeNode(this);
}
///
/// Closest point on the surface of this node to the point p.
///
/// For a point node this is always the node's sicne it has no surface.
///
public override Vector3 ClosestPointOnNode (Vector3 p) {
return (Vector3)this.position;
}
///
/// Checks if point is inside the node when seen from above.
///
/// Since point nodes have no surface area, this method always returns false.
///
public override bool ContainsPoint (Vector3 point) {
return false;
}
///
/// Checks if point is inside the node in graph space.
///
/// Since point nodes have no surface area, this method always returns false.
///
public override bool ContainsPointInGraphSpace (Int3 point) {
return false;
}
public override void GetConnections(GetConnectionsWithData action, ref T data, int connectionFilter) {
if (connections == null) return;
for (int i = 0; i < connections.Length; i++) if ((connections[i].shapeEdgeInfo & connectionFilter) != 0) action(connections[i].node, ref data);
}
public override void ClearConnections (bool alsoReverse) {
if (alsoReverse && connections != null) {
for (int i = 0; i < connections.Length; i++) {
connections[i].node.RemovePartialConnection(this);
}
}
connections = null;
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
}
public override bool ContainsOutgoingConnection (GraphNode node) {
if (connections == null) return false;
for (int i = 0; i < connections.Length; i++) if (connections[i].node == node && connections[i].isOutgoing) return true;
return false;
}
public override void AddPartialConnection (GraphNode node, uint cost, bool isOutgoing, bool isIncoming) {
if (node == null) throw new System.ArgumentNullException();
if (connections != null) {
for (int i = 0; i < connections.Length; i++) {
if (connections[i].node == node) {
connections[i].cost = cost;
connections[i].shapeEdgeInfo = Connection.PackShapeEdgeInfo(isOutgoing, isIncoming);
return;
}
}
}
int connLength = connections != null ? connections.Length : 0;
var newconns = new Connection[connLength+1];
for (int i = 0; i < connLength; i++) {
newconns[i] = connections[i];
}
newconns[connLength] = new Connection(node, cost, isOutgoing, isIncoming);
connections = newconns;
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
// Make sure the graph knows that there exists a connection with this length
if (this.Graph is PointGraph pg) pg.RegisterConnectionLength((node.position - position).sqrMagnitudeLong);
}
public override void RemovePartialConnection (GraphNode node) {
if (connections == null) return;
for (int i = 0; i < connections.Length; i++) {
if (connections[i].node == node) {
int connLength = connections.Length;
var newconns = new Connection[connLength-1];
for (int j = 0; j < i; j++) {
newconns[j] = connections[j];
}
for (int j = i+1; j < connLength; j++) {
newconns[j-1] = connections[j];
}
connections = newconns;
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
return;
}
}
}
public override void Open (Path path, uint pathNodeIndex, uint gScore) {
path.OpenCandidateConnectionsToEndNode(position, pathNodeIndex, pathNodeIndex, gScore);
if (connections == null) return;
for (int i = 0; i < connections.Length; i++) {
GraphNode other = connections[i].node;
if (connections[i].isOutgoing && path.CanTraverse(this, other)) {
if (other is PointNode) {
path.OpenCandidateConnection(pathNodeIndex, other.NodeIndex, gScore, connections[i].cost, 0, other.position);
} else {
// When connecting to a non-point node, use a special function to open the connection.
// The typical case for this is that we are at the end of an off-mesh link and we are connecting to a navmesh node.
// In that case, this node's position is in the interior of the navmesh node. We let the navmesh node decide how
// that should be handled.
other.OpenAtPoint(path, pathNodeIndex, position, gScore);
}
}
}
}
public override void OpenAtPoint (Path path, uint pathNodeIndex, Int3 pos, uint gScore) {
if (path.CanTraverse(this)) {
// TODO: Ideally we should only allow connections to the temporary end node directly from the temporary start node
// iff they lie on the same connection edge. Otherwise we need to pass through the center of this node.
//
// N1---E----N2
// | /
// | /
// S
// |
// N3
//
path.OpenCandidateConnectionsToEndNode(pos, pathNodeIndex, pathNodeIndex, gScore);
var cost = (uint)(pos - this.position).costMagnitude;
path.OpenCandidateConnection(pathNodeIndex, NodeIndex, gScore, cost, 0, position);
}
}
public override int GetGizmoHashCode () {
var hash = base.GetGizmoHashCode();
if (connections != null) {
for (int i = 0; i < connections.Length; i++) {
hash ^= 17 * connections[i].GetHashCode();
}
}
return hash;
}
public override void SerializeNode (GraphSerializationContext ctx) {
base.SerializeNode(ctx);
ctx.SerializeInt3(position);
}
public override void DeserializeNode (GraphSerializationContext ctx) {
base.DeserializeNode(ctx);
position = ctx.DeserializeInt3();
}
public override void SerializeReferences (GraphSerializationContext ctx) {
ctx.SerializeConnections(connections, true);
}
public override void DeserializeReferences (GraphSerializationContext ctx) {
connections = ctx.DeserializeConnections(ctx.meta.version >= AstarSerializer.V4_3_85);
}
}
}