123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- using UnityEngine;
- using System.Collections.Generic;
- namespace Pathfinding {
- using Pathfinding.Util;
- /// <summary>
- /// Connects two nodes via two intermediate point nodes.
- /// In contrast to the NodeLink component, this link type will not connect the nodes directly
- /// instead it will create two point nodes at the start and end position of this link and connect
- /// through those nodes.
- ///
- /// If the closest node to this object is called A and the closest node to the end transform is called
- /// D, then it will create one point node at this object's position (call it B) and one point node at
- /// the position of the end transform (call it C), it will then connect A to B, B to C and C to D.
- ///
- /// This link type is possible to detect while following since it has these special point nodes in the middle.
- /// The link corresponding to one of those intermediate nodes can be retrieved using the <see cref="GetNodeLink"/> method
- /// which can be of great use if you want to, for example, play a link specific animation when reaching the link.
- ///
- /// See: The example scene RecastExample2 contains a few links which you can take a look at to see how they are used.
- /// </summary>
- [AddComponentMenu("Pathfinding/Link2")]
- [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_node_link2.php")]
- public class NodeLink2 : GraphModifier {
- protected static Dictionary<GraphNode, NodeLink2> reference = new Dictionary<GraphNode, NodeLink2>();
- public static NodeLink2 GetNodeLink (GraphNode node) {
- NodeLink2 v;
- reference.TryGetValue(node, out v);
- return v;
- }
- /// <summary>End position of the link</summary>
- public Transform end;
- /// <summary>
- /// The connection will be this times harder/slower to traverse.
- /// Note that values lower than 1 will not always make the pathfinder choose this path instead of another path even though this one should
- /// lead to a lower total cost unless you also adjust the Heuristic Scale in A* Inspector -> Settings -> Pathfinding or disable the heuristic altogether.
- /// </summary>
- public float costFactor = 1.0f;
- /// <summary>Make a one-way connection</summary>
- public bool oneWay = false;
- public Transform StartTransform {
- get { return transform; }
- }
- public Transform EndTransform {
- get { return end; }
- }
- public PointNode startNode { get; private set; }
- public PointNode endNode { get; private set; }
- GraphNode connectedNode1, connectedNode2;
- Vector3 clamped1, clamped2;
- bool postScanCalled = false;
- [System.Obsolete("Use startNode instead (lowercase s)")]
- public GraphNode StartNode {
- get { return startNode; }
- }
- [System.Obsolete("Use endNode instead (lowercase e)")]
- public GraphNode EndNode {
- get { return endNode; }
- }
- public override void OnPostScan () {
- InternalOnPostScan();
- }
- public void InternalOnPostScan () {
- if (EndTransform == null || StartTransform == null) return;
- #if ASTAR_NO_POINT_GRAPH
- throw new System.Exception("Point graph is not included. Check your A* optimization settings.");
- #else
- if (AstarPath.active.data.pointGraph == null) {
- var graph = AstarPath.active.data.AddGraph(typeof(PointGraph)) as PointGraph;
- graph.name = "PointGraph (used for node links)";
- }
- if (startNode != null && startNode.Destroyed) {
- reference.Remove(startNode);
- startNode = null;
- }
- if (endNode != null && endNode.Destroyed) {
- reference.Remove(endNode);
- endNode = null;
- }
- // Create new nodes on the point graph
- if (startNode == null) startNode = AstarPath.active.data.pointGraph.AddNode((Int3)StartTransform.position);
- if (endNode == null) endNode = AstarPath.active.data.pointGraph.AddNode((Int3)EndTransform.position);
- connectedNode1 = null;
- connectedNode2 = null;
- if (startNode == null || endNode == null) {
- startNode = null;
- endNode = null;
- return;
- }
- postScanCalled = true;
- reference[startNode] = this;
- reference[endNode] = this;
- Apply(true);
- #endif
- }
- public override void OnGraphsPostUpdate () {
- // Don't bother running it now since OnPostScan will be called later anyway
- if (AstarPath.active.isScanning)
- return;
- if (connectedNode1 != null && connectedNode1.Destroyed) {
- connectedNode1 = null;
- }
- if (connectedNode2 != null && connectedNode2.Destroyed) {
- connectedNode2 = null;
- }
- if (!postScanCalled) {
- OnPostScan();
- } else {
- Apply(false);
- }
- }
- protected override void OnEnable () {
- base.OnEnable();
- #if !ASTAR_NO_POINT_GRAPH
- if (Application.isPlaying && AstarPath.active != null && AstarPath.active.data != null && AstarPath.active.data.pointGraph != null && !AstarPath.active.isScanning) {
- // Call OnGraphsPostUpdate as soon as possible when it is safe to update the graphs
- AstarPath.active.AddWorkItem(OnGraphsPostUpdate);
- }
- #endif
- }
- protected override void OnDisable () {
- base.OnDisable();
- postScanCalled = false;
- if (startNode != null) reference.Remove(startNode);
- if (endNode != null) reference.Remove(endNode);
- if (startNode != null && endNode != null) {
- startNode.RemoveConnection(endNode);
- endNode.RemoveConnection(startNode);
- if (connectedNode1 != null && connectedNode2 != null) {
- startNode.RemoveConnection(connectedNode1);
- connectedNode1.RemoveConnection(startNode);
- endNode.RemoveConnection(connectedNode2);
- connectedNode2.RemoveConnection(endNode);
- }
- }
- }
- void RemoveConnections (GraphNode node) {
- //TODO, might be better to replace connection
- node.ClearConnections(true);
- }
- [ContextMenu("Recalculate neighbours")]
- void ContextApplyForce () {
- if (Application.isPlaying) {
- Apply(true);
- }
- }
- public void Apply (bool forceNewCheck) {
- //TODO
- //This function assumes that connections from the n1,n2 nodes never need to be removed in the future (e.g because the nodes move or something)
- NNConstraint nn = NNConstraint.None;
- int graph = (int)startNode.GraphIndex;
- //Search all graphs but the one which start and end nodes are on
- nn.graphMask = ~(1 << graph);
- startNode.SetPosition((Int3)StartTransform.position);
- endNode.SetPosition((Int3)EndTransform.position);
- RemoveConnections(startNode);
- RemoveConnections(endNode);
- uint cost = (uint)Mathf.RoundToInt(((Int3)(StartTransform.position-EndTransform.position)).costMagnitude*costFactor);
- startNode.AddConnection(endNode, cost);
- endNode.AddConnection(startNode, cost);
- if (connectedNode1 == null || forceNewCheck) {
- var info = AstarPath.active.GetNearest(StartTransform.position, nn);
- connectedNode1 = info.node;
- clamped1 = info.position;
- }
- if (connectedNode2 == null || forceNewCheck) {
- var info = AstarPath.active.GetNearest(EndTransform.position, nn);
- connectedNode2 = info.node;
- clamped2 = info.position;
- }
- if (connectedNode2 == null || connectedNode1 == null) return;
- //Add connections between nodes, or replace old connections if existing
- connectedNode1.AddConnection(startNode, (uint)Mathf.RoundToInt(((Int3)(clamped1 - StartTransform.position)).costMagnitude*costFactor));
- if (!oneWay) connectedNode2.AddConnection(endNode, (uint)Mathf.RoundToInt(((Int3)(clamped2 - EndTransform.position)).costMagnitude*costFactor));
- if (!oneWay) startNode.AddConnection(connectedNode1, (uint)Mathf.RoundToInt(((Int3)(clamped1 - StartTransform.position)).costMagnitude*costFactor));
- endNode.AddConnection(connectedNode2, (uint)Mathf.RoundToInt(((Int3)(clamped2 - EndTransform.position)).costMagnitude*costFactor));
- }
- private readonly static Color GizmosColor = new Color(206.0f/255.0f, 136.0f/255.0f, 48.0f/255.0f, 0.5f);
- private readonly static Color GizmosColorSelected = new Color(235.0f/255.0f, 123.0f/255.0f, 32.0f/255.0f, 1.0f);
- public virtual void OnDrawGizmosSelected () {
- OnDrawGizmos(true);
- }
- public void OnDrawGizmos () {
- OnDrawGizmos(false);
- }
- public void OnDrawGizmos (bool selected) {
- Color color = selected ? GizmosColorSelected : GizmosColor;
- if (StartTransform != null) {
- Draw.Gizmos.CircleXZ(StartTransform.position, 0.4f, color);
- }
- if (EndTransform != null) {
- Draw.Gizmos.CircleXZ(EndTransform.position, 0.4f, color);
- }
- if (StartTransform != null && EndTransform != null) {
- Draw.Gizmos.Bezier(StartTransform.position, EndTransform.position, color);
- if (selected) {
- Vector3 cross = Vector3.Cross(Vector3.up, (EndTransform.position-StartTransform.position)).normalized;
- Draw.Gizmos.Bezier(StartTransform.position+cross*0.1f, EndTransform.position+cross*0.1f, color);
- Draw.Gizmos.Bezier(StartTransform.position-cross*0.1f, EndTransform.position-cross*0.1f, color);
- }
- }
- }
- internal static void SerializeReferences (Pathfinding.Serialization.GraphSerializationContext ctx) {
- var links = GetModifiersOfType<NodeLink2>();
- ctx.writer.Write(links.Count);
- foreach (var link in links) {
- ctx.writer.Write(link.uniqueID);
- ctx.SerializeNodeReference(link.startNode);
- ctx.SerializeNodeReference(link.endNode);
- ctx.SerializeNodeReference(link.connectedNode1);
- ctx.SerializeNodeReference(link.connectedNode2);
- ctx.SerializeVector3(link.clamped1);
- ctx.SerializeVector3(link.clamped2);
- ctx.writer.Write(link.postScanCalled);
- }
- }
- internal static void DeserializeReferences (Pathfinding.Serialization.GraphSerializationContext ctx) {
- int count = ctx.reader.ReadInt32();
- for (int i = 0; i < count; i++) {
- var linkID = ctx.reader.ReadUInt64();
- var startNode = ctx.DeserializeNodeReference();
- var endNode = ctx.DeserializeNodeReference();
- var connectedNode1 = ctx.DeserializeNodeReference();
- var connectedNode2 = ctx.DeserializeNodeReference();
- var clamped1 = ctx.DeserializeVector3();
- var clamped2 = ctx.DeserializeVector3();
- var postScanCalled = ctx.reader.ReadBoolean();
- GraphModifier link;
- if (usedIDs.TryGetValue(linkID, out link)) {
- var link2 = link as NodeLink2;
- if (link2 != null) {
- if (startNode != null) reference[startNode] = link2;
- if (endNode != null) reference[endNode] = link2;
- // If any nodes happened to be registered right now
- if (link2.startNode != null) reference.Remove(link2.startNode);
- if (link2.endNode != null) reference.Remove(link2.endNode);
- link2.startNode = startNode as PointNode;
- link2.endNode = endNode as PointNode;
- link2.connectedNode1 = connectedNode1;
- link2.connectedNode2 = connectedNode2;
- link2.postScanCalled = postScanCalled;
- link2.clamped1 = clamped1;
- link2.clamped2 = clamped2;
- } else {
- throw new System.Exception("Tried to deserialize a NodeLink2 reference, but the link was not of the correct type or it has been destroyed.\nIf a NodeLink2 is included in serialized graph data, the same NodeLink2 component must be present in the scene when loading the graph data.");
- }
- } else {
- throw new System.Exception("Tried to deserialize a NodeLink2 reference, but the link could not be found in the scene.\nIf a NodeLink2 is included in serialized graph data, the same NodeLink2 component must be present in the scene when loading the graph data.");
- }
- }
- }
- }
- }
|