Ejemplo n.º 1
0
        /** Called when a path has completed it's calculation */
        public void OnPathComplete (Path p) {
		
            /*if (Time.time-lastPathSearch >= repathRate) {
			Repath ();
		} else {*/
            StartCoroutine (WaitToRepath ());
            //}
		
            //If the path didn't succeed, don't proceed
            if (p.error) {
                return;
            }
		
            //Get the calculated path as a Vector3 array
            path = p.vectorPath.ToArray();
		
            //Find the segment in the path which is closest to the AI
            //If a closer segment hasn't been found in '6' iterations, break because it is unlikely to find any closer ones then
            float minDist = Mathf.Infinity;
            int notCloserHits = 0;
		
            for (int i=0;i<path.Length-1;i++) {
                float dist = AstarMath.DistancePointSegmentStrict (path[i],path[i+1],tr.position);
                if (dist < minDist) {
                    notCloserHits = 0;
                    minDist = dist;
                    pathIndex = i+1;
                } else if (notCloserHits > 6) {
                    break;
                }
            }
        }
Ejemplo n.º 2
0
		/** Pops all items from the stack and returns the head.
		 * To loop through all popped items, simple traverse the linked list starting with the head and continuing with item.next until item equals null
		 * \code
		 * Path p = stack.PopAll ();
		 * while (p != null) {
		 * 	//Do something
		 * 	p = p.next;
		 * }
		 * \endcode
		 */
		public Path PopAll () {
#if UNITY_IPHONE || UNITY_PSP2 || UNITY_XBOXONE || UNITY_PS3 || UNITY_PS4 || UNITY_WIIU
			lock (lockObj) {
				Path h = head;
				head = null;
				return h;
			}
#else
			return Interlocked.Exchange<Path> (ref head, null);
#endif
		}
Ejemplo n.º 3
0
		public override void Apply (Path p, ModifierData source) {
			List<GraphNode> path = p.path;
			List<Vector3> vectorPath = p.vectorPath;
			
			if (path == null || path.Count == 0 || vectorPath == null || vectorPath.Count != path.Count) {
				return;
			}
			
			List<Vector3> funnelPath = ListPool<Vector3>.Claim ();
			
			// Claim temporary lists and try to find lists with a high capacity
			List<Vector3> left = ListPool<Vector3>.Claim (path.Count+1);
			List<Vector3> right = ListPool<Vector3>.Claim (path.Count+1);
			
			AstarProfiler.StartProfile ("Construct Funnel");
			
			// Enqueue start point
			left.Add (vectorPath[0]);
			right.Add (vectorPath[0]);
			
			// Loop through all nodes in the path (except the last one)
			for (int i=0;i<path.Count-1;i++) {
				// Get the portal between path[i] and path[i+1] and add it to the left and right lists
				bool portalWasAdded = path[i].GetPortal (path[i+1], left, right, false);
				
				if (!portalWasAdded) {
					// Fallback, just use the positions of the nodes
					left.Add ((Vector3)path[i].position);
					right.Add ((Vector3)path[i].position);
					
					left.Add ((Vector3)path[i+1].position);
					right.Add ((Vector3)path[i+1].position);
				}
			}
			
			// Enqueue end point
			left.Add (vectorPath[vectorPath.Count-1]);
			right.Add (vectorPath[vectorPath.Count-1]);
			
			if (!RunFunnel (left,right,funnelPath)) {
				// If funnel algorithm failed, degrade to simple line
				funnelPath.Add (vectorPath[0]);
				funnelPath.Add (vectorPath[vectorPath.Count-1]);
			}
			
			// Release lists back to the pool
			ListPool<Vector3>.Release (p.vectorPath);
			p.vectorPath = funnelPath;
			
			ListPool<Vector3>.Release (left);
			ListPool<Vector3>.Release (right);
		}
Ejemplo n.º 4
0
		public override void Apply (Path p, ModifierData source) {
			
			if (this == null) return;
			
			lock (lockObject) {
				toBeApplied = p.path.ToArray();
				
				if (!waitingForApply) {
					waitingForApply = true;
					AstarPath.OnPathPreSearch += ApplyNow;
				}
			}
		}
Ejemplo n.º 5
0
		public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
			UpdateG (path,pathNode);
			
			handler.PushNode (pathNode);
			
			for (int i=0;i<connections.Length;i++) {
				GraphNode other = connections[i];
				PathNode otherPN = handler.GetPathNode (other);
				if (otherPN.parent == pathNode && otherPN.pathID == handler.PathID) {
					other.UpdateRecursiveG (path, otherPN,handler);
				}
			}
		}
Ejemplo n.º 6
0
		/** Pushes a path onto the stack.
		  * Will loop while trying to set the head of the stack to \a p. */
		public void Push (Path p) {
#if UNITY_IPHONE || UNITY_PSP2 || UNITY_XBOXONE || UNITY_PS3 || UNITY_PS4 || UNITY_WIIU
			lock (lockObj) {
				p.next = head;
				head = p;
			}
#else
			while (true) {
				p.next = head;
				//Compare head and p.next, if they are equal, set head to p
				Path old = Interlocked.CompareExchange<Path>(ref head, p, p.next);
				//If the exchange suceeded, break. Otherwise, try again
				if (old == p.next) break;
			}
#endif
		}
Ejemplo n.º 7
0
		/** Opens a node using Jump Point Search.
		 * \see http://en.wikipedia.org/wiki/Jump_point_search
		  */
		public void JPSOpen ( Path path, PathNode pathNode, PathHandler handler ) {
			GridGraph gg = GetGridGraph (GraphIndex);
			int[] neighbourOffsets = gg.neighbourOffsets;
			GridNode[] nodes = gg.nodes;
			ushort pid = handler.PathID;

			int noncyclic = gridFlags & 0xFF;
			int cyclic = 0;
			for ( int i = 0; i < 8; i++ ) cyclic |= ((noncyclic >> i)&0x1) << JPSCyclic[i];

			var parent = pathNode.parent != null ? pathNode.parent.node as GridNode : null;
			int parentDir = -1;

			if ( parent != null ) {
				int diff = parent != null ? parent.nodeInGridIndex - nodeInGridIndex : 0;

				int x2 = nodeInGridIndex % gg.width;
				int x1 = parent.nodeInGridIndex % gg.width;
				if ( diff < 0 ) {
					if ( x1 == x2 ) {
						parentDir = 0;
					} else if ( x1 < x2 ) {
						parentDir = 7;
					} else {
						parentDir = 4;
					}
				} else {
					if ( x1 == x2 ) {
						parentDir = 1;
					} else if ( x1 < x2 ) {
						parentDir = 6;
					} else {
						parentDir = 5;
					}
				}
			}
			int cyclicParentDir = 0;
			// Check for -1

			int forced = 0;
			if ( parentDir != -1 ) {
				cyclicParentDir = JPSCyclic[parentDir];
				// Loop around to be able to assume -X is where we came from
				cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;
			} else {
				forced = 0xFF;
				//parentDir = 0;
			}

			bool diagonal = parentDir >= 4;
			int natural;

			if ( diagonal ) {
				for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForcedDiagonal[i];

				natural = JPSNaturalDiagonalNeighbours;
			} else {
				for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForced[i];

				natural = JPSNaturalStraightNeighbours;
			}

			// Don't force nodes we cannot reach anyway
			forced &= cyclic;
			natural &= cyclic;

			int nb = forced | natural;


			/*if ( ((Vector3)position - new Vector3(0.5f,0,3.5f)).magnitude < 0.5f ) {
				Debug.Log (noncyclic + " " + parentDir + " " + cyclicParentDir);
				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));
			}*/

			for (int i=0;i<8;i++) {
				if ( ((nb >> i)&1) != 0 ) {

					int oi = JPSInverseCyclic[(i + cyclicParentDir) % 8];
					GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]];

#if ASTARDEBUG
					if ( ((forced >> i)&1) != 0 ) {
						Debug.DrawLine ( (Vector3)position, Vector3.Lerp ((Vector3)other.position, (Vector3)position, 0.6f), Color.red);
					}
					if ( ((natural >> i)&1) != 0 ) {
						Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f, Vector3.Lerp ((Vector3)other.position, (Vector3)position, 0.6f) + Vector3.up*0.2f, Color.green);
					}
#endif

					if ( oi < 4) {
						other = JPSJumpStraight ( other, path, handler, JPSInverseCyclic[(i + 4 + cyclicParentDir) % 8] );
					} else {
						other = other.JPSJumpDiagonal (path, handler, JPSInverseCyclic[(i + 4 + cyclicParentDir) % 8]);
					}

					if ( other != null ) {
						//Debug.DrawLine ( (Vector3)position + Vector3.up*0.0f, (Vector3)other.position + Vector3.up*0.3f, Color.cyan);
						//Debug.DrawRay ( (Vector3)other.position, Vector3.up, Color.cyan);
						//GridNode other = nodes[nodeInGridIndex + neighbourOffsets[i]];
						//if (!path.CanTraverse (other)) continue;

						PathNode otherPN = handler.GetPathNode (other);

						if (otherPN.pathID != pid) {
							otherPN.parent = pathNode;
							otherPN.pathID = pid;

							otherPN.cost = (uint)(other.position - position).costMagnitude;//neighbourCosts[i];

							otherPN.H = path.CalculateHScore (other);
							other.UpdateG (path, otherPN);

							//Debug.Log ("G " + otherPN.G + " F " + otherPN.F);
							handler.PushNode (otherPN);
							//Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue);
						} else {

							//If not we can test if the path from the current node to this one is a better one then the one already used
							uint tmpCost = (uint)(other.position - position).costMagnitude;//neighbourCosts[i];

							if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G) {
								//Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
								otherPN.cost = tmpCost;

								otherPN.parent = pathNode;

								other.UpdateRecursiveG (path,otherPN, handler);

								//Or if the path from this node ("other") to the current ("current") is better
							} else if (otherPN.G+tmpCost+path.GetTraversalCost (this) < pathNode.G) {
								//Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G);
								pathNode.parent = otherPN;
								pathNode.cost = tmpCost;

								UpdateRecursiveG(path, pathNode, handler);
							}
						}
					}
				}

#if ASTARDEBUG
				if (i == 0 && parentDir != -1 && this.nodeInGridIndex > 10) {
					int oi = JPSInverseCyclic[(i + cyclicParentDir) % 8];

					if ( nodeInGridIndex + neighbourOffsets[oi] < 0 || nodeInGridIndex + neighbourOffsets[oi] >= nodes.Length ) {
						//Debug.LogError ("ERR: " + (nodeInGridIndex + neighbourOffsets[oi]) + " " + cyclicParentDir + " " + parentDir + " Reverted " + oi);
						//Debug.DrawRay ((Vector3)position, Vector3.up, Color.red);
					} else {
						GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]];
						Debug.DrawLine ( (Vector3)position - Vector3.up*0.2f, Vector3.Lerp ((Vector3)other.position, (Vector3)position, 0.6f) - Vector3.up*0.2f, Color.blue);
					}

				}
#endif
			}
		}
Ejemplo n.º 8
0
        /** Starts a path specified by PathTypesDemo.activeDemo */
        void DemoPath () {
		
            Path p = null;
		
            if (activeDemo == DemoMode.ABPath) {
                p = ABPath.Construct (start.position,end.position, OnPathComplete);
			
                if (agents != null && agents.Length > 0) {
                    List<Vector3> pts = ListPool<Vector3>.Claim(agents.Length);
                    Vector3 avg = Vector3.zero;
                    for (int i=0;i<agents.Length;i++) {
                        pts.Add (agents[i].transform.position);
                        avg += pts[i];
                    }
                    avg /= pts.Count;
                    for (int i=0;i<agents.Length;i++) pts[i] -= avg;
				
                    PathUtilities.GetPointsAroundPoint (end.position, AstarPath.active.graphs[0] as IRaycastableGraph, pts, 0, 0.2f);
                    for (int i=0;i<agents.Length;i++) {
                        if (agents[i] == null) continue;
					
                        agents[i].target.position = pts[i];
                        agents[i].UpdatePath();
                    }
                }
            } else if (activeDemo == DemoMode.MultiTargetPath) {
                MultiTargetPath mp = MultiTargetPath.Construct (multipoints.ToArray (), end.position, null, OnPathComplete);
                p = mp;
            } else if (activeDemo == DemoMode.RandomPath) {
                RandomPath rp = RandomPath.Construct (start.position,searchLength, OnPathComplete);
                rp.spread = spread;
                rp.aimStrength = aimStrength;
                rp.aim = end.position;
			
                p = rp;
            } else if (activeDemo == DemoMode.FleePath) {
                FleePath fp = FleePath.Construct (start.position, end.position, searchLength, OnPathComplete);
                fp.aimStrength = aimStrength;
                fp.spread = spread;
			
                p = fp;
            } else if (activeDemo == DemoMode.ConstantPath) {
                StartCoroutine(CalculateConstantPath());
                p = null;
            } else if (activeDemo == DemoMode.FloodPath) {
                FloodPath fp = FloodPath.Construct (end.position, null);
                lastFlood = fp;
                p = fp;
            } else if (activeDemo == DemoMode.FloodPathTracer && lastFlood != null) {
                FloodPathTracer fp = FloodPathTracer.Construct (end.position, lastFlood, OnPathComplete);
			
                p = fp;
            }
		
            if (p != null) {
                AstarPath.StartPath (p);
                lastPath = p;
            }
        }
Ejemplo n.º 9
0
        public override void Apply (Path p, ModifierData source) {
            List<Vector3> vs = p.vectorPath;
		
            List<Vector3> res = Apply (vs);
		
            if (res != vs) {
                ListPool<Vector3>.Release(p.vectorPath);
                p.vectorPath = res;
            }
        }
Ejemplo n.º 10
0
		public void PreProcess (Path p) {
			// Required by IPathModifier
		}
Ejemplo n.º 11
0
		public override void Open (Path path, PathNode pathNode, PathHandler handler) {
			if (connections == null) return;

			for (int i=0;i<connections.Length;i++) {
				GraphNode other = connections[i];

				if (path.CanTraverse (other)) {

					PathNode pathOther = handler.GetPathNode (other);

					if (pathOther.pathID != handler.PathID) {

						pathOther.parent = pathNode;
						pathOther.pathID = handler.PathID;

						pathOther.cost = connectionCosts[i];

						pathOther.H = path.CalculateHScore (other);
						other.UpdateG (path, pathOther);

						handler.PushNode (pathOther);
					} else {
						//If not we can test if the path from this node to the other one is a better one then the one already used
						uint tmpCost = connectionCosts[i];

#if ASTAR_NO_TRAVERSAL_COST
						if (pathNode.G + tmpCost < pathOther.G)
#else
						if (pathNode.G + tmpCost + path.GetTraversalCost(other) < pathOther.G)
#endif
						{

							pathOther.cost = tmpCost;
							pathOther.parent = pathNode;

							other.UpdateRecursiveG (path, pathOther,handler);
						}
#if ASTAR_NO_TRAVERSAL_COST
						else if (pathOther.G+tmpCost < pathNode.G && other.ContainsConnection (this))
#else
						else if (pathOther.G+tmpCost+path.GetTraversalCost(this) < pathNode.G && other.ContainsConnection (this))
#endif
						{
							//Or if the path from the other node to this one is better

							pathNode.parent = pathOther;
							pathNode.cost = tmpCost;

							UpdateRecursiveG (path, pathNode,handler);
						}
					}
				}
			}
		}
Ejemplo n.º 12
0
		public override void Apply (Path p, ModifierData source) {
			//System.DateTime startTime = System.DateTime.UtcNow;
			
			if (iterations <= 0) {
				return;
			}
			
			if (nodes == null) {
				nodes = new List<Vector3> (p.vectorPath.Count);
			} else {
				nodes.Clear ();
			}
			
			nodes.AddRange (p.vectorPath);
			// = new List<Vector3> (p.vectorPath);
			
			for (int it=0;it<iterations;it++) {
				
				if (subdivideEveryIter && it != 0) {
					
					if (nodes.Capacity < nodes.Count*3) {
						nodes.Capacity = nodes.Count*3;
					}
					
					int preLength = nodes.Count;
					
					for (int j=0;j<preLength-1;j++) {
						nodes.Add (Vector3.zero);
						nodes.Add (Vector3.zero);
					}
					
					for (int j=preLength-1;j > 0;j--) {
						
						Vector3 p1 = nodes[j];
						Vector3 p2 = nodes[j+1];
						
						nodes[j*3] = nodes[j];
						
						if (j != preLength-1) {
							nodes[j*3+1] = Vector3.Lerp (p1,p2,0.33F);
							nodes[j*3+2] = Vector3.Lerp (p1,p2,0.66F);
						}
					}
				}
				
				int i = 0;
				while (i < nodes.Count-2) {
					
					Vector3 start = nodes[i];
					Vector3 end = nodes[i+2];
					
					var watch = System.Diagnostics.Stopwatch.StartNew();
					
					if (ValidateLine (null,null,start,end)) {
						nodes.RemoveAt (i+1);
					} else {
						i++;
					}
					
					watch.Stop ();
				}
				
			}
			
			p.vectorPath.Clear ();
			p.vectorPath.AddRange (nodes);
		}
		public override void Open (Path path, PathNode pathNode, PathHandler handler) {

			//BaseOpen (nodeRunData, nodeR, targetPosition, path);

			LayerGridGraph graph = GetGridGraph(GraphIndex);
			int[] neighbourOffsets = graph.neighbourOffsets;
			uint[] neighbourCosts = graph.neighbourCosts;
			LevelGridNode[] nodes = graph.nodes;

			int index = NodeInGridIndex;//indices & 0xFFFFFF;

			for (int i=0;i<4;i++) {
				int conn = GetConnectionValue(i);//(gridConnections >> i*4) & 0xF;
				if (conn != LevelGridNode.NoConnection) {

					GraphNode other = nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn];

					if (!path.CanTraverse (other)) {
						continue;
					}

					PathNode otherPN = handler.GetPathNode (other);

					if (otherPN.pathID != handler.PathID) {
						otherPN.parent = pathNode;
						otherPN.pathID = handler.PathID;

						otherPN.cost = neighbourCosts[i];

						otherPN.H = path.CalculateHScore (other);
						other.UpdateG (path, otherPN);

						handler.PushNode (otherPN);

					} else {
						//If not we can test if the path from the current node to this one is a better one then the one already used
						uint tmpCost = neighbourCosts[i];

#if ASTAR_NO_TRAVERSAL_COST
						if (pathNode.G + tmpCost < otherPN.G)
#else
						if (pathNode.G + tmpCost + path.GetTraversalCost(other) < otherPN.G)
#endif
						{
							otherPN.cost = tmpCost;

							otherPN.parent = pathNode;

							other.UpdateRecursiveG (path,otherPN, handler);

						}
						//Or if the path from this node ("other") to the current ("current") is better
#if ASTAR_NO_TRAVERSAL_COST
						else if (otherPN.G+tmpCost < pathNode.G)
#else
						else if (otherPN.G+tmpCost+path.GetTraversalCost(this) < pathNode.G)
#endif
						{
							pathNode.parent = otherPN;
							pathNode.cost = tmpCost;

							UpdateRecursiveG(path, pathNode, handler);
						}
					}
				}
			}
		}
		public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
			//BaseUpdateAllG (nodeR, nodeRunData);

			handler.PushNode (pathNode);
			UpdateG (path, pathNode);

			LayerGridGraph graph = GetGridGraph (GraphIndex);
			int[] neighbourOffsets = graph.neighbourOffsets;
			LevelGridNode[] nodes = graph.nodes;
			int index = NodeInGridIndex;

			for (int i=0;i<4;i++) {
				int conn = GetConnectionValue(i);//(gridConnections >> i*4) & 0xF;
				if (conn != LevelGridNode.NoConnection) {

					LevelGridNode other = nodes[index+neighbourOffsets[i] + graph.lastScannedWidth*graph.lastScannedDepth*conn];
					PathNode otherPN = handler.GetPathNode (other);

					if (otherPN != null && otherPN.parent == pathNode && otherPN.pathID == handler.PathID) {
						other.UpdateRecursiveG (path, otherPN,handler);
					}
				}
			}
		}
Ejemplo n.º 15
0
        public void OnPathComplete (Path _p) {
            ABPath p = _p as ABPath;
		
            canSearchAgain = true;
		
            if (path != null) path.Release (this);
            path = p;
            p.Claim (this);
		
            if (p.error) {
                wp = 0;
                vectorPath = null;
                return;
            }
		
		
            Vector3 p1 = p.originalStartPoint;
            Vector3 p2 = transform.position;
            p1.y = p2.y;
            float d = (p2-p1).magnitude;
            wp = 0;
		
            vectorPath = p.vectorPath;
            Vector3 waypoint;
		
            for (float t=0;t<=d;t+=moveNextDist*0.6f) {
                wp--;
                Vector3 pos = p1 + (p2-p1)*t;
			
                do {
                    wp++;
                    waypoint = vectorPath[wp];
                    waypoint.y = pos.y;
                } while ((pos - waypoint).sqrMagnitude < moveNextDist*moveNextDist && wp != vectorPath.Count-1);
			
            }
        }
Ejemplo n.º 16
0
		public override void Open (Path path, PathNode pathNode, PathHandler handler) {

			GridGraph gg = GetGridGraph (GraphIndex);

			ushort pid = handler.PathID;

#if ASTAR_JPS
			if ( gg.useJumpPointSearch && !path.FloodingPath ) {
				JPSOpen (path, pathNode, handler);
			} else
#endif
			{
				int[] neighbourOffsets = gg.neighbourOffsets;
				uint[] neighbourCosts = gg.neighbourCosts;
				GridNode[] nodes = gg.nodes;

				for (int i=0;i<8;i++) {
					if (GetConnectionInternal(i)) {

						GridNode other = nodes[nodeInGridIndex + neighbourOffsets[i]];
						if (!path.CanTraverse (other)) continue;

						PathNode otherPN = handler.GetPathNode (other);

						uint tmpCost = neighbourCosts[i];

						if (otherPN.pathID != pid) {
							otherPN.parent = pathNode;
							otherPN.pathID = pid;

							otherPN.cost = tmpCost;

							otherPN.H = path.CalculateHScore (other);
							other.UpdateG (path, otherPN);

							//Debug.Log ("G " + otherPN.G + " F " + otherPN.F);
							handler.PushNode (otherPN);
							//Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue);
						} else {

							// Sorry for the huge number of #ifs

							//If not we can test if the path from the current node to this one is a better one then the one already used

#if ASTAR_NO_TRAVERSAL_COST
							if (pathNode.G+tmpCost < otherPN.G)
#else
							if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G)
#endif
							{
								//Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
								otherPN.cost = tmpCost;

								otherPN.parent = pathNode;

								other.UpdateRecursiveG (path,otherPN, handler);

							//Or if the path from this node ("other") to the current ("current") is better
							}
#if ASTAR_NO_TRAVERSAL_COST
							else if (otherPN.G+tmpCost < pathNode.G)
#else
							else if (otherPN.G+tmpCost+path.GetTraversalCost (this) < pathNode.G)
#endif
							{

								//Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G);
								pathNode.parent = otherPN;
								pathNode.cost = tmpCost;

								UpdateRecursiveG(path, pathNode, handler);
							}
						}
					}
				}
			}

#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
			if (connections != null) for (int i=0;i<connections.Length;i++) {

				GraphNode other = connections[i];
				if (!path.CanTraverse (other)) continue;

				PathNode otherPN = handler.GetPathNode (other);

				uint tmpCost = connectionCosts[i];

				if (otherPN.pathID != pid) {
					otherPN.parent = pathNode;
					otherPN.pathID = pid;

					otherPN.cost = tmpCost;

					otherPN.H = path.CalculateHScore (other);
					other.UpdateG (path, otherPN);

					//Debug.Log ("G " + otherPN.G + " F " + otherPN.F);
					handler.PushNode (otherPN);
					//Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue);
				} else {

					// Sorry for the huge number of #ifs

					//If not we can test if the path from the current node to this one is a better one then the one already used

#if ASTAR_NO_TRAVERSAL_COST
					if (pathNode.G+tmpCost < otherPN.G)
#else
					if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G)
#endif
					{
						//Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
						otherPN.cost = tmpCost;

						otherPN.parent = pathNode;

						other.UpdateRecursiveG (path,otherPN, handler);

					//Or if the path from this node ("other") to the current ("current") is better
					}
#if ASTAR_NO_TRAVERSAL_COST
					else if (otherPN.G+tmpCost < pathNode.G && other.ContainsConnection (this))
#else
					else if (otherPN.G+tmpCost+path.GetTraversalCost (this) < pathNode.G && other.ContainsConnection (this))
#endif
					{
						//Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G);
						pathNode.parent = otherPN;
						pathNode.cost = tmpCost;

						UpdateRecursiveG(path, pathNode, handler);
					}
				}
			}
#endif
		}
Ejemplo n.º 17
0
		void ClearOnDestroy (Path p) {
			lock (lockObject) {
				AstarPath.OnPathPreSearch -= ClearOnDestroy;
				waitingForApply = false;
				InversePrevious ();
			}
		}
Ejemplo n.º 18
0
		//public EndingConditionDistance () {}
		public EndingConditionDistance (Path p, int maxGScore) : base (p) {
			this.maxGScore = maxGScore;
		}
Ejemplo n.º 19
0
		public override void Open (Path path, PathNode pathNode, PathHandler handler) {
			if (connections == null) return;

			// Flag2 indicates if this node needs special treatment
			// with regard to connection costs
			bool flag2 = pathNode.flag2;

			// Loop through all connections
			for (int i=connections.Length-1;i >= 0;i--) {
				GraphNode other = connections[i];

				// Make sure we can traverse the neighbour
				if (path.CanTraverse (other)) {

					PathNode pathOther = handler.GetPathNode (other);

					// Fast path out, worth it for triangle mesh nodes since they usually have degree 2 or 3
					if (pathOther == pathNode.parent) {
						continue;
					}

					uint cost = connectionCosts[i];

					if (flag2 || pathOther.flag2) {
						// Get special connection cost from the path
						// This is used by the start and end nodes
						cost = path.GetConnectionSpecialCost (this,other,cost);
					}

					// Test if we have seen the other node before
					if (pathOther.pathID != handler.PathID) {
						// We have not seen the other node before
						// So the path from the start through this node to the other node
						// must be the shortest one so far

						// Might not be assigned
						pathOther.node = other;

						pathOther.parent = pathNode;
						pathOther.pathID = handler.PathID;

						pathOther.cost = cost;

						pathOther.H = path.CalculateHScore (other);
						other.UpdateG (path, pathOther);

						handler.PushNode (pathOther);
					} else {

						// If not we can test if the path from this node to the other one is a better one than the one already used
						if (pathNode.G + cost + path.GetTraversalCost(other) < pathOther.G) {

							pathOther.cost = cost;
							pathOther.parent = pathNode;

							other.UpdateRecursiveG (path, pathOther,handler);
						}
						else if (pathOther.G+cost+path.GetTraversalCost(this) < pathNode.G && other.ContainsConnection (this)) {
							// Or if the path from the other node to this one is better

							pathNode.parent = pathOther;
							pathNode.cost = cost;

							UpdateRecursiveG (path, pathNode,handler);
						}
					}
				}
			}
		}
Ejemplo n.º 20
0
		public override void Apply (Path _p, ModifierData source) {
			var p = _p as ABPath;
			
			//Only for ABPaths
			if (p == null) return;
			
			if (p.vectorPath.Count == 0) {
				return;
			}

			if (p.vectorPath.Count == 1 && !addPoints) {
				// Duplicate first point
				p.vectorPath.Add (p.vectorPath[0]);
			}
			
			Vector3 pStart = Vector3.zero;
			Vector3 pEnd = Vector3.zero;
			
			switch(exactStartPoint) {
			case Exactness.Original:
				pStart = GetClampedPoint ((Vector3)p.path[0].position, p.originalStartPoint, p.path[0]);
				break;
			case Exactness.ClosestOnNode:
				pStart = GetClampedPoint ((Vector3)p.path[0].position, p.startPoint, p.path[0]);
				break;
			case Exactness.SnapToNode:
				pStart = (Vector3)p.path[0].position;
				break;
			case Exactness.Interpolate:
				pStart = GetClampedPoint ((Vector3)p.path[0].position, p.originalStartPoint, p.path[0]);
				pStart = AstarMath.NearestPointStrict ((Vector3)p.path[0].position,(Vector3)p.path[1>=p.path.Count?0:1].position,pStart);
				break;
			}
			
			switch(exactEndPoint) {
			case Exactness.Original:
				pEnd   = GetClampedPoint ((Vector3)p.path[p.path.Count-1].position, p.originalEndPoint, p.path[p.path.Count-1]);
				break;
			case Exactness.ClosestOnNode:
				pEnd = GetClampedPoint ((Vector3)p.path[p.path.Count-1].position, p.endPoint, p.path[p.path.Count-1]);
				break;
			case Exactness.SnapToNode:
				pEnd = (Vector3)p.path[p.path.Count-1].position;
				break;
			case Exactness.Interpolate:
				pEnd   = GetClampedPoint ((Vector3)p.path[p.path.Count-1].position, p.originalEndPoint, p.path[p.path.Count-1]);
				
				pEnd = AstarMath.NearestPointStrict ((Vector3)p.path[p.path.Count-1].position,(Vector3)p.path[p.path.Count-2<0?0:p.path.Count-2].position,pEnd);
				break;
			}
			
			if (!addPoints) {
				p.vectorPath[0] = pStart;
				p.vectorPath[p.vectorPath.Count-1] = pEnd;
			} else {
				if (exactStartPoint != Exactness.SnapToNode) {
					p.vectorPath.Insert (0,pStart);
				}
				
				if (exactEndPoint != Exactness.SnapToNode) {
					p.vectorPath.Add (pEnd);
				}
			}
			
		}
Ejemplo n.º 21
0
		/** Converts a path from \a input to \a output */
		public static ModifierData Convert (Path p, ModifierData input, ModifierData output) {

			//"Input" can not be converted to "output", log error
			if (!CanConvert (input,output)) {
				Debug.LogError ("Can't convert "+input+" to "+output);
				return ModifierData.None;
			}

			//"Output" can take "input" with no change, return
			if (AnyBits (input,output)) {
				return input;
			}

			//If input is a node path, and output wants a vector array, convert the node array to a vector array
			if (AnyBits (input,ModifierData.Nodes) && AnyBits (output, ModifierData.Vector)) {
				p.vectorPath.Clear();
				for (int i=0;i<p.vectorPath.Count;i++) {
					p.vectorPath.Add ((Vector3)p.path[i].position);
				}

				//Return VectorPath and also StrictVectorPath if input has StrictNodePath set
				return ModifierData.VectorPath | (AnyBits (input, ModifierData.StrictNodePath) ? ModifierData.StrictVectorPath : ModifierData.None);
			}

			Debug.LogError ("This part should not be reached - Error in ModifierConverted\nInput: "+input+" ("+(int)input+")\nOutput: "+output+" ("+(int)output+")");
			return ModifierData.None;
		}
Ejemplo n.º 22
0
		/** Resets End Node Costs. Costs are updated on the end node at the start of the search to better reflect the end point passed to the path, the previous ones are saved in #endNodeCosts and are reset in this function which is called after the path search is complete */
		public void ResetCosts (Path p) {
#if FALSE
			if (!hasEndPoint) return;

			endNode.ResetCosts (endNodeCosts);
#endif
		}
Ejemplo n.º 23
0
		/** Main Post-Processing function */
		public abstract void Apply (Path p, ModifierData source);
Ejemplo n.º 24
0
		/** Reset all values to their default values.
		 *
		 * \note All inheriting path types (e.g ConstantPath, RandomPath, etc.) which declare their own variables need to
		 * override this function, resetting ALL their variables to enable recycling of paths.
		 * If this is not done, trying to use that path type for pooling might result in weird behaviour.
		 * The best way is to reset to default values the variables declared in the extended path type and then
		 * call this base function in inheriting types with base.Reset ().
		 *
		 * \warning This function should not be called manually.
		  */
		public virtual void Reset () {
#if ASTAR_POOL_DEBUG
			pathTraceInfo = "This path was got from the pool or created from here (stacktrace):\n";
			pathTraceInfo += System.Environment.StackTrace;
#endif

			if (System.Object.ReferenceEquals (AstarPath.active, null))
				throw new System.NullReferenceException ("No AstarPath object found in the scene. " +
					"Make sure there is one or do not create paths in Awake");

			hasBeenReset = true;
			state = (int)PathState.Created;
			releasedNotSilent = false;

			pathHandler = null;
			callback = null;
			_errorLog = "";
			pathCompleteState = PathCompleteState.NotCalculated;

			path = ListPool<GraphNode>.Claim();
			vectorPath = ListPool<Vector3>.Claim();

			currentR = null;

			duration = 0;
			searchIterations = 0;
			searchedNodes = 0;
			//calltime

			nnConstraint = PathNNConstraint.Default;
			next = null;

			heuristic = AstarPath.active.heuristic;
			heuristicScale = AstarPath.active.heuristicScale;

			enabledTags = -1;
			tagPenalties = null;

			callTime = System.DateTime.UtcNow;
			pathID = AstarPath.active.GetNextPathID ();

			hTarget = Int3.zero;
			hTargetNode = null;
		}
Ejemplo n.º 25
0
		/** Returns if the node is in the search tree of the path.
		 * Only guaranteed to be correct if \a path is the latest path calculated.
		 * Use for gizmo drawing only.
		 */
		public static bool InSearchTree (GraphNode node, Path path) {
			if (path == null || path.pathHandler == null) return true;
			PathNode nodeR = path.pathHandler.GetPathNode (node);
			return nodeR.pathID == path.pathID;
		}
Ejemplo n.º 26
0
		public PathEndingCondition (Path p) {
			if (p == null) throw new System.ArgumentNullException ("p");
			this.path = p;
		}
Ejemplo n.º 27
0
        /** Get the path back */
        public void OnPathComplete (Path p) {
            //To prevent it from creating new GameObjects when the application is quitting when using multithreading.
            if(lastRender == null) return;
		
            if (p.error) {
                ClearPrevious ();
                return;
            }
		
		
            if (p.GetType () == typeof (MultiTargetPath)) {
			
                List<GameObject> unused = new List<GameObject> (lastRender);
                lastRender.Clear ();
			
                MultiTargetPath mp = p as MultiTargetPath;
			
                for (int i=0;i<mp.vectorPaths.Length;i++) {
                    if (mp.vectorPaths[i] == null) continue;
				
                    List<Vector3> vpath = mp.vectorPaths[i];
				
                    GameObject ob = null;
                    if (unused.Count > i && unused[i].GetComponent<LineRenderer>() != null) {
                        ob = unused[i];
                        unused.RemoveAt (i);
                    } else {
                        ob = new GameObject ("LineRenderer_"+i,typeof(LineRenderer));
                    }
				
                    LineRenderer lr = ob.GetComponent<LineRenderer>();
                    lr.sharedMaterial = lineMat;
                    lr.SetWidth (lineWidth,lineWidth);
				
                    lr.SetVertexCount (vpath.Count);
                    for (int j=0;j<vpath.Count;j++) {
                        lr.SetPosition (j,vpath[j] + pathOffset);
                    }
				
                    lastRender.Add (ob);
                }
			
                for (int i=0;i<unused.Count;i++) {
                    Destroy (unused[i]);
                }
			
            } else if (p.GetType () == typeof (ConstantPath)) {
			
                ClearPrevious ();
                //The following code will build a mesh with a square for each node visited
			
                ConstantPath constPath = p as ConstantPath;
                List<GraphNode> nodes = constPath.allNodes;
			
                Mesh mesh = new Mesh ();

                List<Vector3> verts = new List<Vector3>();
			
                bool drawRaysInstead = false;
			
                // Just some debugging code which selects random points on the nodes
                /*List<Vector3> pts = Pathfinding.PathUtilities.GetPointsOnNodes (nodes, 20, 0);
			Vector3 avg = Vector3.zero;
			for (int i=0;i<pts.Count;i++) {
				Debug.DrawRay (pts[i], Vector3.up*5, Color.red, 3);
				avg += pts[i];
			}
			
			if (pts.Count > 0) avg /= pts.Count;
			
			for (int i=0;i<pts.Count;i++) {
				pts[i] -= avg;
			}
			
			Pathfinding.PathUtilities.GetPointsAroundPoint (start.position, AstarPath.active.astarData.graphs[0] as IRaycastableGraph, pts, 0, 1);
			
			for (int i=0;i<pts.Count;i++) {
				Debug.DrawRay (pts[i], Vector3.up*5, Color.blue, 3);
			}*/
			
                //This will loop through the nodes from furthest away to nearest, not really necessary... but why not :D
                //Note that the reverse does not, as common sense would suggest, loop through from the closest to the furthest away
                //since is might contain duplicates and only the node duplicate placed at the highest index is guarenteed to be ordered correctly.
                for (int i=nodes.Count-1;i>=0;i--) {
				
                    Vector3 pos = (Vector3)nodes[i].position+pathOffset;
                    if (verts.Count	== 65000 && !drawRaysInstead) {
                        Debug.LogError ("Too many nodes, rendering a mesh would throw 65K vertex error. Using Debug.DrawRay instead for the rest of the nodes");
                        drawRaysInstead = true;
                    }
				
                    if (drawRaysInstead) {
                        Debug.DrawRay (pos,Vector3.up,Color.blue);
                        continue;
                    }
				
                    //Enqueue vertices in a square
				
                    GridGraph gg = AstarData.GetGraph (nodes[i]) as GridGraph;
                    float scale = 1F;
				
                    if (gg != null) scale = gg.nodeSize;
				
                    verts.Add (pos+new Vector3 (-0.5F,0,-0.5F)*scale);
                    verts.Add (pos+new Vector3 (0.5F,0,-0.5F)*scale);
                    verts.Add (pos+new Vector3 (-0.5F,0,0.5F)*scale);
                    verts.Add (pos+new Vector3 (0.5F,0,0.5F)*scale);
                }
			
                //Build triangles for the squares
                Vector3[] vs = verts.ToArray ();
                int[] tris = new int[(3*vs.Length)/2];
                for (int i=0, j=0;i<vs.Length;j+=6, i+=4) {
                    tris[j+0] = i;
                    tris[j+1] = i+1;
                    tris[j+2] = i+2;
				
                    tris[j+3] = i+1;
                    tris[j+4] = i+3;
                    tris[j+5] = i+2;
                }
			
                Vector2[] uv = new Vector2[vs.Length];
                //Set up some basic UV
                for (int i=0;i<uv.Length;i+=4) {
                    uv[i] = new Vector2(0,0);
                    uv[i+1] = new Vector2(1,0);
                    uv[i+2] = new Vector2(0,1);
                    uv[i+3] = new Vector2(1,1);
                }
			
                mesh.vertices = vs;
                mesh.triangles = tris;
                mesh.uv = uv;
                mesh.RecalculateNormals ();
			
                GameObject go = new GameObject("Mesh",typeof(MeshRenderer),typeof(MeshFilter));
                MeshFilter fi = go.GetComponent<MeshFilter>();
                fi.mesh = mesh;
                MeshRenderer re = go.GetComponent<MeshRenderer>();
                re.material = squareMat;
			
                lastRender.Add (go);
			
            } else {
			
                ClearPrevious ();
			
                GameObject ob = new GameObject ("LineRenderer",typeof(LineRenderer));
                LineRenderer lr = ob.GetComponent<LineRenderer>();
                lr.sharedMaterial = lineMat;
                lr.SetWidth (lineWidth,lineWidth);
			
                lr.SetVertexCount (p.vectorPath.Count);
                for (int i=0;i<p.vectorPath.Count;i++) {
                    lr.SetPosition (i,p.vectorPath[i] + pathOffset);
                }
			
                lastRender.Add (ob);
            }
        }
Ejemplo n.º 28
0
		void ApplyNow (Path somePath) {
			lock (lockObject) {
				waitingForApply = false;
				AstarPath.OnPathPreSearch -= ApplyNow;
				
				InversePrevious ();
				
				if (destroyed) return;
				
				//Calculate a new seed
				int seed = seedGenerator.Next ();
				rnd = new System.Random (seed);
				
				if (toBeApplied != null) {
					int rndStart = rnd.Next (randomStep);
					for (int i=rndStart;i<toBeApplied.Length;i+= rnd.Next (1,randomStep)) {
						toBeApplied[i].Penalty = (uint)(toBeApplied[i].Penalty+penalty);
					}
				}
				
				prevPenalty = penalty;
				prevSeed = seed;
				prevNodes = toBeApplied;
			}
		}
Ejemplo n.º 29
0
 public IEnumerator CalculateConstantPath () {
     ConstantPath constPath = ConstantPath.Construct (end.position, searchLength, OnPathComplete);
     AstarPath.StartPath (constPath);
     lastPath = constPath;
     yield return constPath.WaitForPath();
 }
Ejemplo n.º 30
0
		/** Executes a diagonal jump search.
		 * \see http://en.wikipedia.org/wiki/Jump_point_search
		  */
		GridNode JPSJumpDiagonal ( Path path, PathHandler handler, int parentDir, int depth=0) {

			// Indexing into the cache arrays from multiple threads like this should cause
			// a lot of false sharing and cache trashing, but after profiling it seems
			// that this is not a major concern
			int threadID = handler.threadID;
			int threadOffset = 8*handler.threadID;

			// This is needed to make sure different threads don't overwrite each others results
			// It doesn't matter if we throw away some caching done by other threads as this will only
			// happen during the first few path requests
			if ( JPSLastCacheID == null || JPSLastCacheID.Length < handler.totalThreadCount ) {
				lock (this) {
					// Check again in case another thread has already created the array
					if ( JPSLastCacheID == null || JPSLastCacheID.Length < handler.totalThreadCount ) {
						JPSCache = new GridNode[8*handler.totalThreadCount];
						JPSDead = new byte[handler.totalThreadCount];
						JPSLastCacheID = new ushort[handler.totalThreadCount];
					}
				}
			}
			if ( JPSLastCacheID[threadID] != path.pathID ) {
				for ( int i = 0; i < 8; i++ ) JPSCache[i + threadOffset] = null;
				JPSLastCacheID[threadID] = path.pathID;
				JPSDead[threadID] = 0;
			}

			// Cache earlier results, major optimization
			// It is important to read from it once and then return the same result,
			// if we read from it twice, we might get different results due to other threads clearing the array sometimes
			GridNode cachedResult = JPSCache[parentDir + threadOffset];
			if ( cachedResult != null ) {
				//return cachedResult;
			}

			//if ( ((JPSDead[threadID] >> parentDir)&1) != 0 ) return null;

			// Special node (e.g end node), take care of
			if ( handler.GetPathNode(this).flag2 ) {
				//Debug.Log ("Found end Node!");
				//Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green);
				JPSCache[parentDir + threadOffset] = this;
				return this;
			}

#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
			// Special node which has custom connections, take care of
			if ( connections != null && connections.Length > 0 ) {
				JPSCache[parentDir] = this;
				return this;
			}
#endif

			int noncyclic = gridFlags;//We don't actually need to & with this because we don't use the other bits. & 0xFF;
			int cyclic = 0;
			for ( int i = 0; i < 8; i++ ) cyclic |= ((noncyclic >> i)&0x1) << JPSCyclic[i];


			int forced = 0;
			int cyclicParentDir = JPSCyclic[parentDir];
			// Loop around to be able to assume -X is where we came from
			cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;

			int natural;

			for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForcedDiagonal[i];

			natural = JPSNaturalDiagonalNeighbours;
			/*
			if ( ((Vector3)position - new Vector3(1.5f,0,-1.5f)).magnitude < 0.5f ) {
				Debug.Log (noncyclic + " " + parentDir + " " + cyclicParentDir);
				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));
			}*/

			// Don't force nodes we cannot reach anyway
			forced &= cyclic;
			natural &= cyclic;

			if ( (forced & (~natural)) != 0 ) {
				// Some of the neighbour nodes are forced
				JPSCache[parentDir+threadOffset] = this;
				return this;
			}

			int forwardDir;

			GridGraph gg = GetGridGraph (GraphIndex);
			int[] neighbourOffsets = gg.neighbourOffsets;
			GridNode[] nodes = gg.nodes;

			{
				// Rotate 180 degrees - 1 node
				forwardDir = 3;
				if ( ((cyclic >> forwardDir)&1) != 0 ) {
					int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8];
					GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]];

					//Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.black);
					GridNode v;
					if ( oi < 4) {
						v = JPSJumpStraight ( other, path, handler, JPSInverseCyclic[(cyclicParentDir-1+8)%8], depth+1 );
					} else {
						v = other.JPSJumpDiagonal ( path, handler, JPSInverseCyclic[(cyclicParentDir-1+8)%8], depth+1 );
					}
					if ( v != null ) {
						JPSCache[parentDir+threadOffset] = this;
						return this;
					}
				}

				// Rotate 180 degrees + 1 node
				forwardDir = 5;
				if ( ((cyclic >> forwardDir)&1) != 0 ) {
					int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8];
					GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]];

					//Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.grey);
					GridNode v;
					if ( oi < 4) {
						v = JPSJumpStraight ( other, path, handler, JPSInverseCyclic[(cyclicParentDir+1+8)%8], depth+1 );
					} else {
						v = other.JPSJumpDiagonal ( path, handler, JPSInverseCyclic[(cyclicParentDir+1+8)%8], depth+1 );
					}

					if ( v != null ) {
						JPSCache[parentDir+threadOffset] = this;
						return this;
					}
				}
			}

			// Rotate 180 degrees
			forwardDir = 4;
			if ( ((cyclic >> forwardDir)&1) != 0 ) {
				int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8];
				GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]];

				//Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta);

				var v = other.JPSJumpDiagonal ( path, handler, parentDir, depth+1 );
				if (v != null) {
					JPSCache[parentDir+threadOffset] = v;
					return v;
				}
			}
			JPSDead[threadID] |= (byte)(1 << parentDir);
			return null;
		}