Inheritance: GridNodeBase
		public override Node[] CreateNodes (int number) {
			
			/*if (nodes != null && graphNodes != null && nodes.Length == number && graphNodes.Length == number) {
				Debug.Log ("Caching");
				return nodes;
			}*/
			
			GridNode[] tmp = new GridNode[number];
			for (int i=0;i<number;i++) {
				tmp[i] = new GridNode ();
			}
			return tmp as Node[];
		}
示例#2
0
		/** Calculates the grid connections for a single node.
		 * Convenience function, it's faster to use CalculateConnections(int,int,GridNode)
		 * but that will only show when calculating for a large number of nodes.
		 * \todo Test this function, should work ok, but you never know
		 */
		public static void CalculateConnections (GridNode node) {
			var gg = AstarData.GetGraph(node) as GridGraph;

			if (gg != null) {
				int index = node.NodeInGridIndex;
				int x = index % gg.width;
				int z = index / gg.width;
				gg.CalculateConnections(x, z, node);
			}
		}
		/** Helper method to set PathNode.flag1 to a specific value for all nodes adjacent to a grid node */
		void SetFlag1OnSurroundingGridNodes (GridNode gridNode, bool flag1State) {
			// Loop through all adjacent grid nodes
			var gridGraph = GridNode.GetGridGraph(gridNode.GraphIndex);

			// Number of neighbours as an int
			int mxnum = gridGraph.neighbours == NumNeighbours.Four ? 4 : (gridGraph.neighbours == NumNeighbours.Eight ? 8 : 6);

			// Calculate the coordinates of the node
			var x = gridNode.NodeInGridIndex % gridGraph.width;
			var z = gridNode.NodeInGridIndex / gridGraph.width;

			for (int i = 0; i < mxnum; i++) {
				int nx, nz;
				if (gridGraph.neighbours == NumNeighbours.Six) {
					// Hexagon graph
					nx = x + gridGraph.neighbourXOffsets[GridGraph.hexagonNeighbourIndices[i]];
					nz = z + gridGraph.neighbourZOffsets[GridGraph.hexagonNeighbourIndices[i]];
				} else {
					nx = x + gridGraph.neighbourXOffsets[i];
					nz = z + gridGraph.neighbourZOffsets[i];
				}

				// Check if the position is still inside the grid
				if (nx >= 0 && nz >= 0 && nx < gridGraph.width && nz < gridGraph.depth) {
					var adjacentNode = gridGraph.nodes[nz*gridGraph.width + nx];
					pathHandler.GetPathNode(adjacentNode).flag1 = flag1State;
				}
			}
		}
		/** Reset all values to their default values.
		 * All inheriting path types must implement this function, resetting ALL their variables to enable recycling of paths.
		 * Call this base function in inheriting types with base.Reset ();
		 */
		public override void Reset () {
			base.Reset ();

			startNode = null;
			endNode = null;
			startHint = null;
			endHint = null;
			originalStartPoint = Vector3.zero;
			originalEndPoint = Vector3.zero;
			startPoint = Vector3.zero;
			endPoint = Vector3.zero;
			calculatePartial = false;
			partialBestTarget = null;
			startIntPoint = new Int3();
			hTarget = new Int3();
			endNodeCosts = null;

#if !ASTAR_NO_GRID_GRAPH
			gridSpecialCaseNode = null;
#endif
		}
		/** Calculates the grid connections for a single node */
		public virtual void CalculateConnections (GridNode[] nodes, int x, int z, GridNode node) {

			//Reset all connections
			// This makes the node have NO connections to any neighbour nodes
			node.ResetConnectionsInternal ();

			//All connections are disabled if the node is not walkable
			if (!node.Walkable) {
				return;
			}

			// Internal index of where in the graph the node is
			int index = node.NodeInGridIndex;

			if (neighbours == NumNeighbours.Four || neighbours == NumNeighbours.Eight) {

				// Reset the buffer
				if (corners == null) {
					corners = new int[4];
				} else {
					for (int i = 0;i<4;i++) {
						corners[i] = 0;
					}
				}

				// Loop through axis aligned neighbours (up, down, right, left)
				for (int i=0, j = 3; i<4; j = i, i++) {

					int nx = x + neighbourXOffsets[i];
					int nz = z + neighbourZOffsets[i];

					if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
						continue;
					}

					var other = nodes[index+neighbourOffsets[i]];

					if (IsValidConnection (node, other)) {
						node.SetConnectionInternal (i, true);

						// Mark the diagonal/corner adjacent to this connection as used
						corners[i]++;
						corners[j]++;
					} else {
						node.SetConnectionInternal (i, false);
					}
				}

				// Add in the diagonal connections
				if (neighbours == NumNeighbours.Eight) {
					if (cutCorners) {
						for (int i=0; i<4; i++) {

							// If at least one axis aligned connection
							// is adjacent to this diagonal, then we can add a connection
							if (corners[i] >= 1) {
								int nx = x + neighbourXOffsets[i+4];
								int nz = z + neighbourZOffsets[i+4];

								if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
									continue;
								}

								GridNode other = nodes[index+neighbourOffsets[i+4]];

								node.SetConnectionInternal (i+4, IsValidConnection (node,other));
							}
						}
					} else {
						for (int i=0; i<4; i++) {

							// If exactly 2 axis aligned connections is adjacent to this connection
							// then we can add the connection
							//We don't need to check if it is out of bounds because if both of the other neighbours are inside the bounds this one must be too
							if (corners[i] == 2) {
								GridNode other = nodes[index+neighbourOffsets[i+4]];

								node.SetConnectionInternal (i+4, IsValidConnection (node,other));
							}
						}
					}
				}
			} else {
				// Hexagon layout

				// Loop through all possible neighbours and try to connect to them
				for (int j = 0; j < hexagonNeighbourIndices.Length; j++) {
					var i = hexagonNeighbourIndices[j];

					int nx = x + neighbourXOffsets[i];
					int nz = z + neighbourZOffsets[i];

					if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
						continue;
					}

					var other = nodes[index+neighbourOffsets[i]];

					node.SetConnectionInternal (i, IsValidConnection (node, other));
				}
			}
		}
示例#6
0
			/** Applies a value to the node using the specified ChannelUse */
			void ApplyChannel (GridNode node, int x, int z, int value, ChannelUse channelUse, float factor) {
				switch (channelUse) {
				case ChannelUse.Penalty:
					node.Penalty += (uint)Mathf.RoundToInt (value*factor);
					break;
				case ChannelUse.Position:
					node.position = GridNode.GetGridGraph(node.GraphIndex).GraphPointToWorld (x, z, value);
					break;
				case ChannelUse.WalkablePenalty:
					if (value == 0) {
						node.Walkable = false;
					} else {
						node.Penalty += (uint)Mathf.RoundToInt ((value-1)*factor);
					}
					break;
				}
			}
示例#7
0
		/** True if the node has any blocked connections.
		 * For 4 and 8 neighbours the 4 axis aligned connections will be checked.
		 * For 6 neighbours all 6 neighbours will be checked.
		 */
		bool ErosionAnyFalseConnections (GridNode node) {
			if (neighbours == NumNeighbours.Six) {
				// Check the 6 hexagonal connections
				for (int i = 0; i < 6; i++) {
					if (!HasNodeConnection(node, hexagonNeighbourIndices[i])) {
						return true;
					}
				}
			} else {
				// Check the four axis aligned connections
				for (int i = 0; i < 4; i++) {
					if (!HasNodeConnection(node, i)) {
						return true;
					}
				}
			}

			return false;
		}
示例#8
0
		public bool HasNodeConnection (GridNode node, int dir) {
			if (!node.GetConnectionInternal(dir)) return false;
			if (!node.EdgeNode) {
				return true;
			} else {
				int index = node.NodeInGridIndex;
				int z = index/Width;
				int x = index - z*Width;

				return HasNodeConnection(index, x, z, dir);
			}
		}
示例#9
0
        public override bool GetPortal(GraphNode other, List <Vector3> left, List <Vector3> right, bool backwards)
        {
            if (backwards)
            {
                return(true);
            }

            GridGraph gg = GetGridGraph(GraphIndex);

            int[]      neighbourOffsets = gg.neighbourOffsets;
            GridNode[] nodes            = gg.nodes;

            for (int i = 0; i < 4; i++)
            {
                if (GetConnectionInternal(i) && other == nodes[nodeInGridIndex + neighbourOffsets[i]])
                {
                    Vector3 middle = ((Vector3)(position + other.position)) * 0.5f;
                    Vector3 cross  = Vector3.Cross(gg.collision.up, (Vector3)(other.position - position));
                    cross.Normalize();
                    cross *= gg.nodeSize * 0.5f;
                    left.Add(middle - cross);
                    right.Add(middle + cross);
                    return(true);
                }
            }

            for (int i = 4; i < 8; i++)
            {
                if (GetConnectionInternal(i) && other == nodes[nodeInGridIndex + neighbourOffsets[i]])
                {
                    bool rClear = false;
                    bool lClear = false;
                    if (GetConnectionInternal(i - 4))
                    {
                        GridNode n2 = nodes[nodeInGridIndex + neighbourOffsets[i - 4]];
                        if (n2.Walkable && n2.GetConnectionInternal((i - 4 + 1) % 4))
                        {
                            rClear = true;
                        }
                    }

                    if (GetConnectionInternal((i - 4 + 1) % 4))
                    {
                        GridNode n2 = nodes[nodeInGridIndex + neighbourOffsets[(i - 4 + 1) % 4]];
                        if (n2.Walkable && n2.GetConnectionInternal(i - 4))
                        {
                            lClear = true;
                        }
                    }

                    Vector3 middle = ((Vector3)(position + other.position)) * 0.5f;
                    Vector3 cross  = Vector3.Cross(gg.collision.up, (Vector3)(other.position - position));
                    cross.Normalize();
                    cross *= gg.nodeSize * 1.4142f;
                    left.Add(middle - (lClear ? cross : Vector3.zero));
                    right.Add(middle + (rClear ? cross : Vector3.zero));
                    return(true);
                }
            }

            return(false);
        }
示例#10
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
        }
        public override void Open(Path path, PathNode pathNode, PathHandler handler)
        {
            GridGraph gg = GetGridGraph(GraphIndex);

            int[]      neighbourOffsets = gg.neighbourOffsets;
            uint[]     neighbourCosts   = gg.neighbourCosts;
            GridNode[] nodes            = gg.nodes;
            ushort     pid = handler.PathID;

            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);

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

                        otherPN.cost = 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 = 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);
                        }
                    }
                }
            }
        }
示例#12
0
        /** Returns randomly selected points on the specified nodes with each point being separated by \a clearanceRadius from each other.
         * Selecting points ON the nodes only works for TriangleMeshNode (used by Recast Graph and Navmesh Graph) and GridNode (used by GridGraph).
         * For other node types, only the positions of the nodes will be used.
         *
         * clearanceRadius will be reduced if no valid points can be found.
         */
        public static List <Vector3> GetPointsOnNodes(List <GraphNode> nodes, int count, float clearanceRadius = 0)
        {
            if (nodes == null)
            {
                throw new System.ArgumentNullException("nodes");
            }
            if (nodes.Count == 0)
            {
                throw new System.ArgumentException("no nodes passed");
            }

            System.Random rnd = new System.Random();

            List <Vector3> pts = Pathfinding.Util.ListPool <Vector3> .Claim(count);

            // Square
            clearanceRadius *= clearanceRadius;

            if (nodes[0] is TriangleMeshNode || nodes[0] is GridNode)
            {
                //Assume all nodes are triangle nodes or grid nodes

                List <float> accs = Pathfinding.Util.ListPool <float> .Claim(nodes.Count);

                float tot = 0;

                for (int i = 0; i < nodes.Count; i++)
                {
                    TriangleMeshNode tnode = nodes[i] as TriangleMeshNode;
                    if (tnode != null)
                    {
                        float a = System.Math.Abs(Polygon.TriangleArea(tnode.GetVertex(0), tnode.GetVertex(1), tnode.GetVertex(2)));
                        tot += a;
                        accs.Add(tot);
                    }
#if !ASTAR_NO_GRID_GRAPH
                    else
                    {
                        GridNode gnode = nodes[i] as GridNode;

                        if (gnode != null)
                        {
                            GridGraph gg = GridNode.GetGridGraph(gnode.GraphIndex);
                            float     a  = gg.nodeSize * gg.nodeSize;
                            tot += a;
                            accs.Add(tot);
                        }
                        else
                        {
                            accs.Add(tot);
                        }
                    }
#endif
                }

                for (int i = 0; i < count; i++)
                {
                    //Pick point
                    int  testCount = 0;
                    int  testLimit = 10;
                    bool worked    = false;

                    while (!worked)
                    {
                        worked = true;

                        //If no valid points can be found, progressively lower the clearance radius until such a point is found
                        if (testCount >= testLimit)
                        {
                            clearanceRadius *= 0.8f;
                            testLimit       += 10;
                            if (testLimit > 100)
                            {
                                clearanceRadius = 0;
                            }
                        }

                        float tg = (float)rnd.NextDouble() * tot;
                        int   v  = accs.BinarySearch(tg);
                        if (v < 0)
                        {
                            v = ~v;
                        }

                        if (v >= nodes.Count)
                        {
                            // This shouldn't happen, due to NextDouble being smaller than 1... but I don't trust floating point arithmetic.
                            worked = false;
                            continue;
                        }

                        TriangleMeshNode node = nodes[v] as TriangleMeshNode;

                        Vector3 p;

                        if (node != null)
                        {
                            // Find a random point inside the triangle
                            float v1;
                            float v2;
                            do
                            {
                                v1 = (float)rnd.NextDouble();
                                v2 = (float)rnd.NextDouble();
                            } while (v1 + v2 > 1);

                            p = ((Vector3)(node.GetVertex(1) - node.GetVertex(0))) * v1 + ((Vector3)(node.GetVertex(2) - node.GetVertex(0))) * v2 + (Vector3)node.GetVertex(0);
                        }
                        else
                        {
#if !ASTAR_NO_GRID_GRAPH
                            GridNode gnode = nodes[v] as GridNode;

                            if (gnode != null)
                            {
                                GridGraph gg = GridNode.GetGridGraph(gnode.GraphIndex);

                                float v1 = (float)rnd.NextDouble();
                                float v2 = (float)rnd.NextDouble();
                                p = (Vector3)gnode.position + new Vector3(v1 - 0.5f, 0, v2 - 0.5f) * gg.nodeSize;
                            }
                            else
#endif
                            {
                                //Point nodes have no area, so we break directly instead
                                pts.Add((Vector3)nodes[v].position);
                                break;
                            }
                        }

                        // Test if it is some distance away from the other points
                        if (clearanceRadius > 0)
                        {
                            for (int j = 0; j < pts.Count; j++)
                            {
                                if ((pts[j] - p).sqrMagnitude < clearanceRadius)
                                {
                                    worked = false;
                                    break;
                                }
                            }
                        }

                        if (worked)
                        {
                            pts.Add(p);
                            break;
                        }
                        else
                        {
                            testCount++;
                        }
                    }
                }

                Pathfinding.Util.ListPool <float> .Release(accs);
            }
            else
            {
                for (int i = 0; i < count; i++)
                {
                    pts.Add((Vector3)nodes[rnd.Next(nodes.Count)].position);
                }
            }

            return(pts);
        }
示例#13
0
        protected virtual bool EndPointGridGraphSpecialCase(GraphNode closestWalkableEndNode)
        {
            GridNode gridNode = closestWalkableEndNode as GridNode;

            if (gridNode != null)
            {
                GridGraph gridGraph = GridNode.GetGridGraph(gridNode.GraphIndex);
                GridNode  gridNode2 = AstarPath.active.GetNearest(this.originalEndPoint, NNConstraint.None).node as GridNode;
                if (gridNode != gridNode2 && gridNode2 != null && gridNode.GraphIndex == gridNode2.GraphIndex)
                {
                    int  num  = gridNode.NodeInGridIndex % gridGraph.width;
                    int  num2 = gridNode.NodeInGridIndex / gridGraph.width;
                    int  num3 = gridNode2.NodeInGridIndex % gridGraph.width;
                    int  num4 = gridNode2.NodeInGridIndex / gridGraph.width;
                    bool flag = false;
                    switch (gridGraph.neighbours)
                    {
                    case NumNeighbours.Four:
                        if ((num == num3 && Math.Abs(num2 - num4) == 1) || (num2 == num4 && Math.Abs(num - num3) == 1))
                        {
                            flag = true;
                        }
                        break;

                    case NumNeighbours.Eight:
                        if (Math.Abs(num - num3) <= 1 && Math.Abs(num2 - num4) <= 1)
                        {
                            flag = true;
                        }
                        break;

                    case NumNeighbours.Six:
                        for (int i = 0; i < 6; i++)
                        {
                            int num5 = num3 + gridGraph.neighbourXOffsets[GridGraph.hexagonNeighbourIndices[i]];
                            int num6 = num4 + gridGraph.neighbourZOffsets[GridGraph.hexagonNeighbourIndices[i]];
                            if (num == num5 && num2 == num6)
                            {
                                flag = true;
                                break;
                            }
                        }
                        break;

                    default:
                        throw new Exception("Unhandled NumNeighbours");
                    }
                    if (flag)
                    {
                        this.SetFlagOnSurroundingGridNodes(gridNode2, 1, true);
                        this.endPoint            = (Vector3)gridNode2.position;
                        this.hTarget             = gridNode2.position;
                        this.endNode             = gridNode2;
                        this.hTargetNode         = this.endNode;
                        this.gridSpecialCaseNode = gridNode2;
                        return(true);
                    }
                }
            }
            return(false);
        }
示例#14
0
        public override float SurfaceArea()
        {
            GridGraph gg = GridNode.GetGridGraph(GraphIndex);

            return(gg.nodeSize * gg.nodeSize);
        }
示例#15
0
		/** Calculates the grid connections for a single node.
		 * The x and z parameters are assumed to be the grid coordinates of the node.
		 *
		 * \see CalculateConnections(GridNode)
		 */
		public virtual void CalculateConnections (int x, int z, GridNode node) {
			// All connections are disabled if the node is not walkable
			if (!node.Walkable) {
				// Reset all connections
				// This makes the node have NO connections to any neighbour nodes
				node.ResetConnectionsInternal();
				return;
			}

			// Internal index of where in the graph the node is
			int index = node.NodeInGridIndex;

			if (neighbours == NumNeighbours.Four || neighbours == NumNeighbours.Eight) {
				// Bitpacked connections
				// bit 0 is set if connection 0 is enabled
				// bit 1 is set if connection 1 is enabled etc.
				int conns = 0;

				// Loop through axis aligned neighbours (down, right, up, left) or (-Z, +X, +Z, -X)
				for (int i = 0; i < 4; i++) {
					int nx = x + neighbourXOffsets[i];
					int nz = z + neighbourZOffsets[i];

					// Check if the new position is inside the grid
					// Bitwise AND (&) is measurably faster than &&
					// (not much, but this code is hot)
					if (nx >= 0 & nz >= 0 & nx < width & nz < depth) {
						var other = nodes[index+neighbourOffsets[i]];

						if (IsValidConnection(node, other)) {
							// Enable connection i
							conns |= 1 << i;
						}
					}
				}

				// Bitpacked diagonal connections
				int diagConns = 0;

				// Add in the diagonal connections
				if (neighbours == NumNeighbours.Eight) {
					if (cutCorners) {
						for (int i = 0; i < 4; i++) {
							// If at least one axis aligned connection
							// is adjacent to this diagonal, then we can add a connection.
							// Bitshifting is a lot faster than calling node.GetConnectionInternal.
							// We need to check if connection i and i+1 are enabled
							// but i+1 may overflow 4 and in that case need to be wrapped around
							// (so 3+1 = 4 goes to 0). We do that by checking both connection i+1
							// and i+1-4 at the same time. Either i+1 or i+1-4 will be in the range
							// from 0 to 4 (exclusive)
							if (((conns >> i | conns >> (i+1) | conns >> (i+1-4)) & 1) != 0) {
								int directionIndex = i+4;

								int nx = x + neighbourXOffsets[directionIndex];
								int nz = z + neighbourZOffsets[directionIndex];

								if (nx >= 0 & nz >= 0 & nx < width & nz < depth) {
									GridNode other = nodes[index+neighbourOffsets[directionIndex]];

									if (IsValidConnection(node, other)) {
										diagConns |= 1 << directionIndex;
									}
								}
							}
						}
					} else {
						for (int i = 0; i < 4; i++) {
							// If exactly 2 axis aligned connections is adjacent to this connection
							// then we can add the connection
							// We don't need to check if it is out of bounds because if both of
							// the other neighbours are inside the bounds this one must be too
							if ((conns >> i & 1) != 0 && ((conns >> (i+1) | conns >> (i+1-4)) & 1) != 0) {
								GridNode other = nodes[index+neighbourOffsets[i+4]];

								if (IsValidConnection(node, other)) {
									diagConns |= 1 << (i+4);
								}
							}
						}
					}
				}

				// Set all connections at the same time
				node.SetAllConnectionInternal(conns | diagConns);
			} else {
				// Hexagon layout

				// Reset all connections
				// This makes the node have NO connections to any neighbour nodes
				node.ResetConnectionsInternal();

				// Loop through all possible neighbours and try to connect to them
				for (int j = 0; j < hexagonNeighbourIndices.Length; j++) {
					var i = hexagonNeighbourIndices[j];

					int nx = x + neighbourXOffsets[i];
					int nz = z + neighbourZOffsets[i];

					if (nx >= 0 & nz >= 0 & nx < width & nz < depth) {
						var other = nodes[index+neighbourOffsets[i]];
						node.SetConnectionInternal(i, IsValidConnection(node, other));
					}
				}
			}
		}
示例#16
0
		public override void DeserializeExtraInfo (GraphSerializationContext ctx) {
			int count = ctx.reader.ReadInt32();

			if (count == -1) {
				nodes = null;
				return;
			}

			nodes = new GridNode[count];

			for (int i = 0; i < nodes.Length; i++) {
				nodes[i] = new GridNode(active);
				nodes[i].DeserializeNode(ctx);
			}
		}
示例#17
0
        /** Executes a straight jump search.
         * \see http://en.wikipedia.org/wiki/Jump_point_search
         */
        static GridNode JPSJumpStraight(GridNode node, Path path, PathHandler handler, int parentDir, int depth = 0)
        {
            GridGraph gg = GetGridGraph(node.GraphIndex);

            int[]      neighbourOffsets = gg.neighbourOffsets;
            GridNode[] nodes            = gg.nodes;

            GridNode origin = node;
            // 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;

            int cyclicParentDir = JPSCyclic[parentDir];

            GridNode result = null;

            // Rotate 180 degrees
            const int forwardDir    = 4;
            int       forwardOffset = neighbourOffsets[JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]];

            // Move forwards in the same direction
            // until a node is encountered which we either
            // * know the result for (memoization)
            // * is a special node (flag2 set)
            // * has custom connections
            // * the node has a forced neighbour
            // Then break out of the loop
            // and start another loop which goes through the same nodes and sets the
            // memoization caches to avoid expensive calls in the future
            while (true)
            {
                // 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 (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount)
                {
                    lock (node) {
                        // Check again in case another thread has already created the array
                        if (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount)
                        {
                            node.JPSCache       = new GridNode[8 * handler.totalThreadCount];
                            node.JPSDead        = new byte[handler.totalThreadCount];
                            node.JPSLastCacheID = new ushort[handler.totalThreadCount];
                        }
                    }
                }
                if (node.JPSLastCacheID[threadID] != path.pathID)
                {
                    for (int i = 0; i < 8; i++)
                    {
                        node.JPSCache[i + threadOffset] = null;
                    }
                    node.JPSLastCacheID[threadID] = path.pathID;
                    node.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 = node.JPSCache[parentDir + threadOffset];
                if (cachedResult != null)
                {
                    result = cachedResult;
                    break;
                }

                if (((node.JPSDead[threadID] >> parentDir) & 1) != 0)
                {
                    return(null);
                }

                // Special node (e.g end node), take care of
                if (handler.GetPathNode(node).flag2)
                {
                    //Debug.Log ("Found end Node!");
                    //Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green);
                    result = node;
                    break;
                }

                                #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
                // Special node which has custom connections, take care of
                if (node.connections != null && node.connections.Length > 0)
                {
                    result = node;
                    break;
                }
                                #endif


                // These are the nodes this node is connected to, one bit for each of the 8 directions
                int noncyclic = node.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;
                // Loop around to be able to assume -X is where we came from
                cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;

                //for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForced[i];
                if ((cyclic & (1 << 2)) == 0)
                {
                    forced |= (1 << 3);
                }
                if ((cyclic & (1 << 6)) == 0)
                {
                    forced |= (1 << 5);
                }

                int natural = JPSNaturalStraightNeighbours;

                // Check if there are any forced neighbours which we can reach that are not natural neighbours
                //if ( ((forced & cyclic) & (~(natural & cyclic))) != 0 ) {
                if ((forced & (~natural) & cyclic) != 0)
                {
                    // Some of the neighbour nodes are forced
                    result = node;
                    break;
                }

                // Make sure we can reach the next node
                if ((cyclic & (1 << forwardDir)) != 0)
                {
                    node = nodes[node.nodeInGridIndex + forwardOffset];

                    //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta);
                }
                else
                {
                    result = null;
                    break;
                }
            }

            if (result == null)
            {
                while (origin != node)
                {
                    origin.JPSDead[threadID] |= (byte)(1 << parentDir);
                    origin = nodes[origin.nodeInGridIndex + forwardOffset];
                }
            }
            else
            {
                while (origin != node)
                {
                    origin.JPSCache[parentDir + threadOffset] = result;
                    origin = nodes[origin.nodeInGridIndex + forwardOffset];
                }
            }

            return(result);
        }
示例#18
0
		public override void ScanInternal (OnScanStatus statusCallback) {
			AstarPath.OnPostScan += new OnScanDelegate(OnPostScan);

			if (nodeSize <= 0) {
				return;
			}

			// Make sure the matrix is up to date
			GenerateMatrix();

			if (width > 1024 || depth > 1024) {
				Debug.LogError("One of the grid's sides is longer than 1024 nodes");
				return;
			}


			SetUpOffsetsAndCosts();

			// Get the graph index of this graph
			int graphIndex = AstarPath.active.astarData.GetGraphIndex(this);

			// Set a global reference to this graph so that nodes can find it
			GridNode.SetGridGraph(graphIndex, this);

			// Create all nodes
			nodes = new GridNode[width*depth];
			for (int i = 0; i < nodes.Length; i++) {
				nodes[i] = new GridNode(active);
				nodes[i].GraphIndex = (uint)graphIndex;
			}

			// Create and initialize the collision class
			if (collision == null) {
				collision = new GraphCollision();
			}
			collision.Initialize(matrix, nodeSize);


			for (int z = 0; z < depth; z++) {
				for (int x = 0; x < width; x++) {
					var node = nodes[z*width+x];

					node.NodeInGridIndex = z*width+x;

					// Updates the position of the node
					// and a bunch of other things
					UpdateNodePositionCollision(node, x, z);
				}
			}

			for (int z = 0; z < depth; z++) {
				for (int x = 0; x < width; x++) {
					var node = nodes[z*width + x];
					// Recalculate connections to other nodes
					CalculateConnections(x, z, node);
				}
			}

			// Apply erosion
			ErodeWalkableArea();
		}
示例#19
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);
        }
示例#20
0
        public static List <Vector3> GetPointsOnNodes(List <GraphNode> nodes, int count, float clearanceRadius = 0f)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (nodes.Count == 0)
            {
                throw new ArgumentException("no nodes passed");
            }
            System.Random  random = new System.Random();
            List <Vector3> list   = ListPool <Vector3> .Claim(count);

            clearanceRadius *= clearanceRadius;
            if (nodes[0] is TriangleMeshNode || nodes[0] is GridNode)
            {
                List <float> list2 = ListPool <float> .Claim(nodes.Count);

                float num = 0f;
                for (int i = 0; i < nodes.Count; i++)
                {
                    TriangleMeshNode triangleMeshNode = nodes[i] as TriangleMeshNode;
                    if (triangleMeshNode != null)
                    {
                        float num2 = (float)Math.Abs(Polygon.TriangleArea2(triangleMeshNode.GetVertex(0), triangleMeshNode.GetVertex(1), triangleMeshNode.GetVertex(2)));
                        num += num2;
                        list2.Add(num);
                    }
                    else
                    {
                        GridNode gridNode = nodes[i] as GridNode;
                        if (gridNode != null)
                        {
                            GridGraph gridGraph = GridNode.GetGridGraph(gridNode.GraphIndex);
                            float     num3      = gridGraph.nodeSize * gridGraph.nodeSize;
                            num += num3;
                            list2.Add(num);
                        }
                        else
                        {
                            list2.Add(num);
                        }
                    }
                }
                for (int j = 0; j < count; j++)
                {
                    int  num4 = 0;
                    int  num5 = 10;
                    bool flag = false;
                    while (!flag)
                    {
                        flag = true;
                        if (num4 >= num5)
                        {
                            clearanceRadius *= 0.8f;
                            num5            += 10;
                            if (num5 > 100)
                            {
                                clearanceRadius = 0f;
                            }
                        }
                        float item = (float)random.NextDouble() * num;
                        int   num6 = list2.BinarySearch(item);
                        if (num6 < 0)
                        {
                            num6 = ~num6;
                        }
                        if (num6 >= nodes.Count)
                        {
                            flag = false;
                        }
                        else
                        {
                            TriangleMeshNode triangleMeshNode2 = nodes[num6] as TriangleMeshNode;
                            Vector3          vector;
                            if (triangleMeshNode2 != null)
                            {
                                float num7;
                                float num8;
                                do
                                {
                                    num7 = (float)random.NextDouble();
                                    num8 = (float)random.NextDouble();
                                }while (num7 + num8 > 1f);
                                vector = (Vector3)(triangleMeshNode2.GetVertex(1) - triangleMeshNode2.GetVertex(0)) * num7 + (Vector3)(triangleMeshNode2.GetVertex(2) - triangleMeshNode2.GetVertex(0)) * num8 + (Vector3)triangleMeshNode2.GetVertex(0);
                            }
                            else
                            {
                                GridNode gridNode2 = nodes[num6] as GridNode;
                                if (gridNode2 == null)
                                {
                                    list.Add((Vector3)nodes[num6].position);
                                    break;
                                }
                                GridGraph gridGraph2 = GridNode.GetGridGraph(gridNode2.GraphIndex);
                                float     num9       = (float)random.NextDouble();
                                float     num10      = (float)random.NextDouble();
                                vector = (Vector3)gridNode2.position + new Vector3(num9 - 0.5f, 0f, num10 - 0.5f) * gridGraph2.nodeSize;
                            }
                            if (clearanceRadius > 0f)
                            {
                                for (int k = 0; k < list.Count; k++)
                                {
                                    if ((list[k] - vector).sqrMagnitude < clearanceRadius)
                                    {
                                        flag = false;
                                        break;
                                    }
                                }
                            }
                            if (flag)
                            {
                                list.Add(vector);
                                break;
                            }
                            num4++;
                        }
                    }
                }
                ListPool <float> .Release(list2);
            }
            else
            {
                for (int l = 0; l < count; l++)
                {
                    list.Add((Vector3)nodes[random.Next(nodes.Count)].position);
                }
            }
            return(list);
        }
示例#21
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
            }
        }
		/** Returns true if a connection between the adjacent nodes \a n1 and \a n2 is valid.
		 * Also takes into account if the nodes are walkable
		 *
		 * This method may be overriden if you want to customize what connections are valid.
		 * It must however hold that IsValidConnection(a,b) == IsValidConnection(b,a)
		 */
		public virtual bool IsValidConnection (GridNode n1, GridNode n2) {
			if (!n1.Walkable || !n2.Walkable) {
				return false;
			}

			if (maxClimb > 0 && Mathf.Abs (n1.position[maxClimbAxis] - n2.position[maxClimbAxis]) > maxClimb*Int3.Precision) {
				return false;
			}

			return true;
		}
示例#23
0
        /** Applies a special case for grid nodes.
         *
         * Assume the closest walkable node is a grid node.
         * We will now apply a special case only for grid graphs.
         * In tile based games, an obstacle often occupies a whole
         * node. When a path is requested to the position of an obstacle
         * (single unwalkable node) the closest walkable node will be
         * one of the 8 nodes surrounding that unwalkable node
         * but that node is not neccessarily the one that is most
         * optimal to walk to so in this special case
         * we mark all nodes around the unwalkable node as targets
         * and when we search and find any one of them we simply exit
         * and set that first node we found to be the 'real' end node
         * because that will be the optimal node (this does not apply
         * in general unless the heuristic is set to None, but
         * for a single unwalkable node it does).
         * This also applies if the nearest node cannot be traversed for
         * some other reason like restricted tags.
         *
         * \returns True if the workaround was applied. If this happens the
         * endPoint, endNode, hTarget and hTargetNode fields will be modified.
         *
         * Image below shows paths when this special case is applied. The path goes from the white sphere to the orange box.
         * \shadowimage{abpath_grid_special.gif}
         *
         * Image below shows paths when this special case has been disabled
         * \shadowimage{abpath_grid_not_special.gif}
         */
        protected virtual bool EndPointGridGraphSpecialCase(GraphNode closestWalkableEndNode)
        {
            var gridNode = closestWalkableEndNode as GridNode;

            if (gridNode != null)
            {
                var gridGraph = GridNode.GetGridGraph(gridNode.GraphIndex);

                // Find the closest node, not neccessarily walkable
                var endNNInfo2 = AstarPath.active.GetNearest(originalEndPoint, NNConstraint.None);
                var gridNode2  = endNNInfo2.node as GridNode;

                if (gridNode != gridNode2 && gridNode2 != null && gridNode.GraphIndex == gridNode2.GraphIndex)
                {
                    // Calculate the coordinates of the nodes
                    var x1 = gridNode.NodeInGridIndex % gridGraph.width;
                    var z1 = gridNode.NodeInGridIndex / gridGraph.width;

                    var x2 = gridNode2.NodeInGridIndex % gridGraph.width;
                    var z2 = gridNode2.NodeInGridIndex / gridGraph.width;

                    bool wasClose = false;
                    switch (gridGraph.neighbours)
                    {
                    case NumNeighbours.Four:
                        if ((x1 == x2 && System.Math.Abs(z1 - z2) == 1) || (z1 == z2 && System.Math.Abs(x1 - x2) == 1))
                        {
                            // If 'O' is gridNode2, then gridNode is one of the nodes marked with an 'x'
                            //    x
                            //  x O x
                            //    x
                            wasClose = true;
                        }
                        break;

                    case NumNeighbours.Eight:
                        if (System.Math.Abs(x1 - x2) <= 1 && System.Math.Abs(z1 - z2) <= 1)
                        {
                            // If 'O' is gridNode2, then gridNode is one of the nodes marked with an 'x'
                            //  x x x
                            //  x O x
                            //  x x x
                            wasClose = true;
                        }
                        break;

                    case NumNeighbours.Six:
                        // Hexagon graph
                        for (int i = 0; i < 6; i++)
                        {
                            var nx = x2 + gridGraph.neighbourXOffsets[GridGraph.hexagonNeighbourIndices[i]];
                            var nz = z2 + gridGraph.neighbourZOffsets[GridGraph.hexagonNeighbourIndices[i]];
                            if (x1 == nx && z1 == nz)
                            {
                                // If 'O' is gridNode2, then gridNode is one of the nodes marked with an 'x'
                                //    x x
                                //  x O x
                                //  x x
                                wasClose = true;
                                break;
                            }
                        }
                        break;

                    default:
                        // Should not happen unless NumNeighbours is modified in the future
                        throw new System.Exception("Unhandled NumNeighbours");
                    }

                    if (wasClose)
                    {
                        // We now need to find all nodes marked with an x to be able to mark them as targets
                        SetFlagOnSurroundingGridNodes(gridNode2, 1, true);

                        // Note, other methods assume hTarget is (Int3)endPoint
                        endPoint = (Vector3)gridNode2.position;
                        hTarget  = gridNode2.position;
                        endNode  = gridNode2;

                        // hTargetNode is used for heuristic optimizations
                        // (also known as euclidean embedding).
                        // Even though the endNode is not walkable
                        // we can use it for better heuristics since
                        // there is a workaround added (EuclideanEmbedding.ApplyGridGraphEndpointSpecialCase)
                        // which is there to support this case.
                        hTargetNode = endNode;

                        // We need to save this node
                        // so that we can reset flag1 on all nodes later
                        gridSpecialCaseNode = gridNode2;
                        return(true);
                    }
                }
            }

            return(false);
        }
示例#24
0
		public override IEnumerable<Progress> ScanInternal () {

			AstarPath.OnPostScan += new OnScanDelegate (OnPostScan);

			if (nodeSize <= 0) {
				yield break;
			}

			// Make sure the matrix is up to date
			GenerateMatrix ();

			if (width > 1024 || depth > 1024) {
				Debug.LogError ("One of the grid's sides is longer than 1024 nodes");
				yield break;
			}


			SetUpOffsetsAndCosts ();

			// Get the graph index of this graph
			int graphIndex = AstarPath.active.astarData.GetGraphIndex(this);

			// Set a global reference to this graph so that nodes can find it
			GridNode.SetGridGraph (graphIndex,this);

			yield return new Progress(0.05f, "Creating nodes");

			// Create all nodes
			nodes = new GridNode[width*depth];
			for (int i = 0; i < nodes.Length; i++) {
				nodes[i] = new GridNode(active);
				nodes[i].GraphIndex = (uint)graphIndex;
			}

			// Create and initialize the collision class
			if (collision == null) {
				collision = new GraphCollision ();
			}
			collision.Initialize (matrix,nodeSize);


			int progressCounter = 0;

			const int YieldEveryNNodes = 1000;

			for (int z = 0; z < depth; z ++) {
				// Yield with a progress value at most every N nodes
				if (progressCounter >= YieldEveryNNodes) {
					progressCounter = 0;
					yield return new Progress(Mathf.Lerp(0.1f,0.7f,z/(float)depth), "Calculating positions");
				}

				progressCounter += width;

				for (int x = 0; x < width; x++) {

					var node = nodes[z*width+x];

					node.NodeInGridIndex = z*width+x;

					// Updates the position of the node
					// and a bunch of other things
					UpdateNodePositionCollision (node,x,z);

				}
			}

			for (int z = 0; z < depth; z++) {
				// Yield with a progress value at most every N nodes
				if (progressCounter >= YieldEveryNNodes) {
					progressCounter = 0;
					yield return new Progress(Mathf.Lerp(0.1f,0.7f,z/(float)depth), "Calculating connections");
				}

				for (int x = 0; x < width; x++) {
					var node = nodes[z*width + x];
					// Recalculate connections to other nodes
					CalculateConnections(x, z, node);
				}
			}

			yield return new Progress(0.95f, "Calculating erosion");

			// Apply erosion
			ErodeWalkableArea ();
		}
示例#25
0
		public override void ScanInternal (OnScanStatus statusCallback) {
			
			AstarPath.OnPostScan += new OnScanDelegate (OnPostScan);
			
			scans++;
			
			if (nodeSize <= 0) {
				return;
			}
			
			GenerateMatrix ();
			
			if (width > 1024 || depth > 1024) {
				Debug.LogError ("One of the grid's sides is longer than 1024 nodes");
				return;
			}

			
			SetUpOffsetsAndCosts ();
			
			var graphIndex = AstarPath.active.astarData.GetGraphIndex(this);
			GridNode.SetGridGraph (graphIndex,this);

			nodes = new GridNode[width*depth];
			for (var i=0;i<nodes.Length;i++) {
				nodes[i] = new GridNode(active);
				nodes[i].GraphIndex = (uint)graphIndex;
			}
			
			if (collision == null) {
				collision = new GraphCollision ();
			}
			collision.Initialize (matrix,nodeSize);
			
			
			for (var z = 0; z < depth; z ++) {
				for (var x = 0; x < width; x++) {
					
					var node = nodes[z*width+x];
					
					node.NodeInGridIndex = z*width+x;
					
					UpdateNodePositionCollision (node,x,z);
					
				}
			}
			
			
			for (var z = 0; z < depth; z ++) {
				for (var x = 0; x < width; x++) {
				
					var node = nodes[z*width+x];
						
					CalculateConnections (nodes,x,z,node);
					
				}
			}
			
			
			ErodeWalkableArea ();
		}
示例#26
0
		/** Applies a special case for grid nodes.
		 *
		 * Assume the closest walkable node is a grid node.
		 * We will now apply a special case only for grid graphs.
		 * In tile based games, an obstacle often occupies a whole
		 * node. When a path is requested to the position of an obstacle
		 * (single unwalkable node) the closest walkable node will be
		 * one of the 8 nodes surrounding that unwalkable node
		 * but that node is not neccessarily the one that is most
		 * optimal to walk to so in this special case
		 * we mark all nodes around the unwalkable node as targets
		 * and when we search and find any one of them we simply exit
		 * and set that first node we found to be the 'real' end node
		 * because that will be the optimal node (this does not apply
		 * in general unless the heuristic is set to None, but
		 * for a single unwalkable node it does).
		 * This also applies if the nearest node cannot be traversed for
		 * some other reason like restricted tags.
		 *
		 * \returns True if the workaround was applied. If this happens the
		 * endPoint, endNode, hTarget and hTargetNode fields will be modified.
		 *
		 * Image below shows paths when this special case is applied. The path goes from the white sphere to the blue orange box.
		 * \shadowimage{abpath_grid_special.gif}
		 *
		 * Image below shows paths when this special case has been disabled
		 * \shadowimage{abpath_grid_not_special.gif}
		 */
		protected virtual bool EndPointGridGraphSpecialCase (GraphNode closestWalkableEndNode) {
			var gridNode = closestWalkableEndNode as GridNode;
			if (gridNode != null) {
				var gridGraph = GridNode.GetGridGraph(gridNode.GraphIndex);

				// Find the closest node, not neccessarily walkable
				NNInfo endNNInfo2 = AstarPath.active.GetNearest(originalEndPoint, NNConstraint.None, endHint);
				var gridNode2 = endNNInfo2.node as GridNode;

				if (gridNode != gridNode2 && gridNode2 != null && gridNode.GraphIndex == gridNode2.GraphIndex) {
					// Calculate the coordinates of the nodes
					var x1 = gridNode.NodeInGridIndex % gridGraph.width;
					var z1 = gridNode.NodeInGridIndex / gridGraph.width;

					var x2 = gridNode2.NodeInGridIndex % gridGraph.width;
					var z2 = gridNode2.NodeInGridIndex / gridGraph.width;

					bool wasClose = false;
					switch (gridGraph.neighbours) {
					case NumNeighbours.Four:
						if ((x1 == x2 && System.Math.Abs(z1-z2) == 1) || (z1 == z2 && System.Math.Abs(x1-x2) == 1)) {
							// If 'O' is gridNode2, then gridNode is one of the nodes marked with an 'x'
							//    x
							//  x O x
							//    x
							wasClose = true;
						}
						break;
					case NumNeighbours.Eight:
						if (System.Math.Abs(x1-x2) <= 1 && System.Math.Abs(z1-z2) <= 1) {
							// If 'O' is gridNode2, then gridNode is one of the nodes marked with an 'x'
							//  x x x
							//  x O x
							//  x x x
							wasClose = true;
						}
						break;
					case NumNeighbours.Six:
						// Hexagon graph
						for (int i = 0; i < 6; i++) {
							var nx = x2 + gridGraph.neighbourXOffsets[GridGraph.hexagonNeighbourIndices[i]];
							var nz = z2 + gridGraph.neighbourZOffsets[GridGraph.hexagonNeighbourIndices[i]];
							if (x1 == nx && z1 == nz) {
								// If 'O' is gridNode2, then gridNode is one of the nodes marked with an 'x'
								//    x x
								//  x O x
								//  x x
								wasClose = true;
								break;
							}
						}
						break;
					default:
						// Should not happen unless NumNeighbours is modified in the future
						throw new System.Exception("Unhandled NumNeighbours");
					}

					if (wasClose) {
						// We now need to find all nodes marked with an x to be able to mark them as targets
						SetFlag1OnSurroundingGridNodes(gridNode2, true);

						// Note, other methods assume hTarget is (Int3)endPoint
						endPoint = (Vector3)gridNode2.position;
						hTarget = gridNode2.position;
						endNode = gridNode2;

						// hTargetNode is used for heuristic optimizations
						// (also known as euclidean embedding).
						// Even though the endNode is not walkable
						// we can use it for better heuristics since
						// there is a workaround added (EuclideanEmbedding.ApplyGridGraphEndpointSpecialCase)
						// which is there to support this case.
						hTargetNode = endNode;

						// We need to save this node
						// so that we can reset flag1 on all nodes later
						gridSpecialCaseNode = gridNode2;
						return true;
					}
				}
			}

			return false;
		}
示例#27
0
		/** Updates position, walkability and penalty for the node.
		 * Assumes that collision.Initialize (...) has been called before this function */
		public virtual void UpdateNodePositionCollision (GridNode node, int x, int z, bool resetPenalty = true) {
			
			node.position = GetNodePosition ( node.NodeInGridIndex, 0 );//0;// = (Int3)matrix.MultiplyPoint3x4 (new Vector3 (x+0.5F,0,z+0.5F));
			
			RaycastHit hit;
			
			var walkable = true;

			var position = collision.CheckHeight ((Vector3)node.position, out hit, out walkable);
			node.position = (Int3)position;
			
			if (resetPenalty)
				node.Penalty = initialPenalty;
			
			if (penaltyPosition && resetPenalty) {
				node.Penalty += (uint)Mathf.RoundToInt ((node.position.y-penaltyPositionOffset)*penaltyPositionFactor);
			}

			//Check if the node is on a slope steeper than permitted
			if (walkable && useRaycastNormal && collision.heightCheck) {
				
				if (hit.normal != Vector3.zero) {
					//Take the dot product to find out the cosinus of the angle it has (faster than Vector3.Angle)
					var angle = Vector3.Dot (hit.normal.normalized,collision.up);
					if ( Mathf.Abs ( 1 - collision.up.magnitude ) > 0.1f ) {
						Debug.Log ("HI");
					}

					//Add penalty based on normal
					if (penaltyAngle && resetPenalty) {
						node.Penalty += (uint)Mathf.RoundToInt ((1F-Mathf.Pow(angle, penaltyAnglePower))*penaltyAngleFactor);
					}
					
					//Max slope in cosinus
					var cosAngle = Mathf.Cos (maxSlope*Mathf.Deg2Rad);
					
					//Check if the slope is flat enough to stand on
					if (angle < cosAngle) {
						walkable = false;
					}
				}
			}
			
			//If the walkable flag has already been set to false, there is no point in checking for it again
			if (walkable)
				node.Walkable = collision.Check ((Vector3)node.position);
			else
				node.Walkable = walkable;
			
			node.WalkableErosion = node.Walkable;
		}
示例#28
0
		/** Returns true if a connection between the adjacent nodes \a n1 and \a n2 is valid.
		 * Also takes into account if the nodes are walkable.
		 *
		 * This method may be overriden if you want to customize what connections are valid.
		 * It must however hold that IsValidConnection(a,b) == IsValidConnection(b,a).
		 *
		 * This is used for calculating the connections when the graph is scanned or updated.
		 *
		 * \see CalculateConnections
		 */
		public virtual bool IsValidConnection (GridNode n1, GridNode n2) {
			if (!n1.Walkable || !n2.Walkable) {
				return false;
			}

			return maxClimb <= 0 || System.Math.Abs(n1.position[maxClimbAxis] - n2.position[maxClimbAxis]) <= maxClimb*Int3.Precision;
		}
示例#29
0
		/** Calculates the grid connections for a single node */
		public virtual void CalculateConnections (GridNode[] nodes, int x, int z, GridNode node) {
			
			//Reset all connections
			// This makes the node have NO connections to any neighbour nodes
			node.ResetConnectionsInternal ();
			
			//All connections are disabled if the node is not walkable
			if (!node.Walkable) {
				return;
			}

			// Internal index of where in the graph the node is
			var index = node.NodeInGridIndex;

			if (corners == null) {
				corners = new int[4];
			} else {
				for (var i = 0;i<4;i++) {
					corners[i] = 0;
				}
			}

			// Loop through axis aligned neighbours (up, down, right, left)
			for (int i=0, j = 3; i<4; j = i, i++) {
				
				var nx = x + neighbourXOffsets[i];
				var nz = z + neighbourZOffsets[i];
				
				if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
					continue;
				}
				
				var other = nodes[index+neighbourOffsets[i]] as GridNode;
				
				if (IsValidConnection (node, other)) {
					node.SetConnectionInternal (i, true);
					corners[i]++;
					corners[j]++;
				} else {
					node.SetConnectionInternal (i, false);
				}
			}

			// Add in the diagonal connections
			if (neighbours == NumNeighbours.Eight) {
				if (cutCorners) {
					for (var i=0; i<4; i++) {
						
						if (corners[i] >= 1) {
							var nx = x + neighbourXOffsets[i+4];
							var nz = z + neighbourZOffsets[i+4];
						
							if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
								continue;
							}
					
							var other = nodes[index+neighbourOffsets[i+4]];
							
							node.SetConnectionInternal (i+4, IsValidConnection (node,other));
						}
					}
				} else {
					for (var i=0; i<4; i++) {
						
						//We don't need to check if it is out of bounds because if both of the other neighbours are inside the bounds this one must be too
						if (corners[i] == 2) {
							var other = nodes[index+neighbourOffsets[i+4]];
							
							node.SetConnectionInternal (i+4, IsValidConnection (node,other));
						}
					}
				}
			}
			
		}
示例#30
0
		public virtual void CalculateConnections (GridNode[] nodes, int x, int z, GridNode node) {
			CalculateConnections(x, z, node);
		}
示例#31
0
		public void AddPortal (GridNode n1, GridNode n2, List<Vector3> left, List<Vector3> right) {
			
			if (n1 == n2) {
				return;
			}
			
			int i1 = n1.GetIndex ();
			int i2 = n2.GetIndex ();
			int x1 = i1 % width;
			int x2 = i2 % width;
			int z1 = i1 / width;
			int z2 = i2 / width;
			
			Vector3 n1p = n1.position;
			Vector3 n2p = n2.position;
			
			int diffx = Mathf.Abs (x1-x2);
			int diffz = Mathf.Abs (z1-z2);
			
			if (diffx > 1 || diffz > 1) {
				//If the nodes are not adjacent to each other
				
				left.Add (n1p);
				right.Add (n1p);
				left.Add (n2p);
				right.Add (n2p);
			} else if ((diffx+diffz) <= 1){
				//If it is not a diagonal move
				
				Vector3 dir = n2p - n1p;
				dir = dir.normalized * nodeSize * 0.5F;
				Vector3 tangent = Vector3.Cross (dir, Vector3.up);
				tangent = tangent.normalized * nodeSize * 0.5F;
				
				left.Add (n1p + dir - tangent);
				right.Add (n1p + dir + tangent);
			} else {
				//Diagonal move
				
				Node t1 = nodes[z1 * width + x2];
				Node t2 = nodes[z2 * width + x1];
				Node target = null;
				
				if (t1.walkable) {
					target = t1;
				} else if (t2.walkable) {
					target = t2;
				}
				
				if (target == null) {
					Vector3 avg = (n1p + n2p) * 0.5F;
					
					left.Add (avg);
					right.Add (avg);
				} else {
					AddPortal (n1,(GridNode)target,left,right);
					AddPortal ((GridNode)target,n2,left,right);
				}
			}
		}
示例#32
0
		/** Returns if \a node is connected to it's neighbour in the specified direction.
		 * This will also return true if #neighbours = NumNeighbours.Four, the direction is diagonal and one can move through one of the adjacent nodes
		 * to the targeted node.
		 *
		 * \see neighbourOffsets
		 */
		public bool CheckConnection (GridNode node, int dir) {
			if (neighbours == NumNeighbours.Eight || neighbours == NumNeighbours.Six || dir < 4) {
				return HasNodeConnection(node, dir);
			} else {
				int dir1 = (dir-4-1) & 0x3;
				int dir2 = (dir-4+1) & 0x3;

				if (!HasNodeConnection(node, dir1) || !HasNodeConnection(node, dir2)) {
					return false;
				} else {
					GridNode n1 = nodes[node.NodeInGridIndex+neighbourOffsets[dir1]];
					GridNode n2 = nodes[node.NodeInGridIndex+neighbourOffsets[dir2]];

					if (!n1.Walkable || !n2.Walkable) {
						return false;
					}

					if (!HasNodeConnection(n2, dir1) || !HasNodeConnection(n1, dir2)) {
						return false;
					}
				}
				return true;
			}
		}
示例#33
0
		//END IFunnelGraph Implementation
		
		
		public bool CheckConnection (GridNode node, int dir) {
			if (neighbours == NumNeighbours.Eight) {
				return node.GetConnection (dir);
			} else {
				int dir1 = (dir-4-1) & 0x3;
				int dir2 = (dir-4+1) & 0x3;
				
				if (!node.GetConnection (dir1) || !node.GetConnection (dir2)) {
					return false;
				} else {
					GridNode n1 = nodes[node.GetIndex ()+neighbourOffsets[dir1]] as GridNode;
					GridNode n2 = nodes[node.GetIndex ()+neighbourOffsets[dir2]] as GridNode;
					
					if (!n1.walkable || !n2.walkable) {
						return false;
					}
					
					if (!n2.GetConnection (dir1) || !n1.GetConnection (dir2)) {
						return false;
					}
				}
				return true;
			}
		}
示例#34
0
		public GridNode GetNodeConnection (GridNode node, int dir) {
			if (!node.GetConnectionInternal(dir)) return null;
			if (!node.EdgeNode) {
				return nodes[node.NodeInGridIndex + neighbourOffsets[dir]];
			} else {
				int index = node.NodeInGridIndex;
				//int z = Math.DivRem (index,Width, out x);
				int z = index/Width;
				int x = index - z*Width;

				return GetNodeConnection(index, x, z, dir);
			}
		}
示例#35
0
		/** Calculates the grid connections for a single node. Convenience function, it's faster to use CalculateConnections(GridNode[],int,int,node) but that will only show when calculating for a large number of nodes
		  * \todo Test this function, should work ok, but you never know */
		public static void CalculateConnections (GridNode node) {
			GridGraph gg = AstarData.GetGraph (node) as GridGraph;
			
			if (gg != null) {
				int index = node.GetIndex ();
				int x = index % gg.width;
				int z = index / gg.width;
				gg.CalculateConnections (gg.graphNodes,x,z,node);
			}
		}
示例#36
0
		public void SetNodeConnection (GridNode node, int dir, bool value) {
			int index = node.NodeInGridIndex;
			int z = index/Width;
			int x = index - z*Width;

			SetNodeConnection(index, x, z, dir, value);
		}
示例#37
0
		/** Calculates the grid connections for a single node */
		public virtual void CalculateConnections (GridNode[] graphNodes, int x, int z, GridNode node) {
			
			//Reset all connections
			node.flags = node.flags & -256;
			
			//All connections are disabled if the node is not walkable
			if (!node.walkable) {
				return;
			}
			
			int index = node.GetIndex ();
			
			if (corners == null) {
				corners = new int[4];
			} else {
				for (int i = 0;i<4;i++) {
					corners[i] = 0;
				}
			}
			
			for (int i=0, j = 3; i<4; j = i, i++) {
				
				int nx = x + neighbourXOffsets[i];
				int nz = z + neighbourZOffsets[i];
				
				if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
					continue;
				}
				
				GridNode other = graphNodes[index+neighbourOffsets[i]];
				
				if (IsValidConnection (node, other)) {
					node.SetConnectionRaw (i,1);
					
					corners[i]++;
					corners[j]++;
				}
			}
			
			if (neighbours == NumNeighbours.Eight) {
				if (cutCorners) {
					for (int i=0; i<4; i++) {
						
						if (corners[i] >= 1) {
							int nx = x + neighbourXOffsets[i+4];
							int nz = z + neighbourZOffsets[i+4];
						
							if (nx < 0 || nz < 0 || nx >= width || nz >= depth) {
								continue;
							}
					
							GridNode other = graphNodes[index+neighbourOffsets[i+4]];
							
							if (IsValidConnection (node,other)) {
								node.SetConnectionRaw (i+4,1);
							}
						}
					}
				} else {
					for (int i=0; i<4; i++) {
						
						//We don't need to check if it is out of bounds because if both of the other neighbours are inside the bounds this one must be too
						if (corners[i] == 2) {
							GridNode other = graphNodes[index+neighbourOffsets[i+4]];
							
							if (IsValidConnection (node,other)) {
								node.SetConnectionRaw (i+4,1);
							}
						}
					}
				}
			}
			
		}
示例#38
0
		/** Updates position, walkability and penalty for the node.
		 * Assumes that collision.Initialize (...) has been called before this function */
		public virtual void UpdateNodePositionCollision (GridNode node, int x, int z, bool resetPenalty = true) {
			// Set the node's initial position with a y-offset of zero
			node.position = GraphPointToWorld(x, z, 0);

			RaycastHit hit;

			bool walkable;

			// Calculate the actual position using physics raycasting (if enabled)
			// walkable will be set to false if no ground was found (unless that setting has been disabled)
			Vector3 position = collision.CheckHeight((Vector3)node.position, out hit, out walkable);
			node.position = (Int3)position;

			if (resetPenalty) {
				node.Penalty = initialPenalty;

				// Calculate a penalty based on the y coordinate of the node
				if (penaltyPosition) {
					node.Penalty += (uint)Mathf.RoundToInt((node.position.y-penaltyPositionOffset)*penaltyPositionFactor);
				}
			}

			// Check if the node is on a slope steeper than permitted
			if (walkable && useRaycastNormal && collision.heightCheck) {
				if (hit.normal != Vector3.zero) {
					// Take the dot product to find out the cosinus of the angle it has (faster than Vector3.Angle)
					float angle = Vector3.Dot(hit.normal.normalized, collision.up);

					// Add penalty based on normal
					if (penaltyAngle && resetPenalty) {
						node.Penalty += (uint)Mathf.RoundToInt((1F-Mathf.Pow(angle, penaltyAnglePower))*penaltyAngleFactor);
					}

					// Cosinus of the max slope
					float cosAngle = Mathf.Cos(maxSlope*Mathf.Deg2Rad);

					// Check if the ground is flat enough to stand on
					if (angle < cosAngle) {
						walkable = false;
					}
				}
			}

			// If the walkable flag has already been set to false, there is no point in checking for it again
			// Check for obstacles
			node.Walkable = walkable && collision.Check((Vector3)node.position);

			// Store walkability before erosion is applied
			// Used for graph updating
			node.WalkableErosion = node.Walkable;
		}
示例#39
0
			/** Applies the texture to the node */
			public void Apply (GridNode node, int x, int z) {
				if (enabled && data != null && x < source.width && z < source.height) {
					Color32 col = data[z*source.width+x];

					if (channels[0] != ChannelUse.None) {
						ApplyChannel (node,x,z,col.r,channels[0],factors[0]);
					}

					if (channels[1] != ChannelUse.None) {
						ApplyChannel (node,x,z,col.g,channels[1],factors[1]);
					}

					if (channels[2] != ChannelUse.None) {
						ApplyChannel (node,x,z,col.b,channels[2],factors[2]);
					}
				}
			}
示例#40
0
		/** Executes a straight jump search.
		 * \see http://en.wikipedia.org/wiki/Jump_point_search
		 */
		static GridNode JPSJumpStraight (GridNode node, Path path, PathHandler handler, int parentDir, int depth = 0) {
			GridGraph gg = GetGridGraph(node.GraphIndex);

			int[] neighbourOffsets = gg.neighbourOffsets;
			GridNode[] nodes = gg.nodes;

			GridNode origin = node;
			// 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;

			int cyclicParentDir = JPSCyclic[parentDir];

			GridNode result = null;

			// Rotate 180 degrees
			const int forwardDir = 4;
			int forwardOffset = neighbourOffsets[JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]];

			// Move forwards in the same direction
			// until a node is encountered which we either
			// * know the result for (memoization)
			// * is a special node (flag2 set)
			// * has custom connections
			// * the node has a forced neighbour
			// Then break out of the loop
			// and start another loop which goes through the same nodes and sets the
			// memoization caches to avoid expensive calls in the future
			while (true) {
				// 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 (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount) {
					lock (node) {
						// Check again in case another thread has already created the array
						if (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount) {
							node.JPSCache = new GridNode[8*handler.totalThreadCount];
							node.JPSDead = new byte[handler.totalThreadCount];
							node.JPSLastCacheID = new ushort[handler.totalThreadCount];
						}
					}
				}
				if (node.JPSLastCacheID[threadID] != path.pathID) {
					for (int i = 0; i < 8; i++) node.JPSCache[i + threadOffset] = null;
					node.JPSLastCacheID[threadID] = path.pathID;
					node.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 = node.JPSCache[parentDir + threadOffset];
				if (cachedResult != null) {
					result = cachedResult;
					break;
				}

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

				// Special node (e.g end node), take care of
				if (handler.GetPathNode(node).flag2) {
					//Debug.Log ("Found end Node!");
					//Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green);
					result = node;
					break;
				}

				#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
				// Special node which has custom connections, take care of
				if (node.connections != null && node.connections.Length > 0) {
					result = node;
					break;
				}
				#endif


				// These are the nodes this node is connected to, one bit for each of the 8 directions
				int noncyclic = node.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;
				// Loop around to be able to assume -X is where we came from
				cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF;

				//for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForced[i];
				if ((cyclic & (1 << 2)) == 0) forced |= (1<<3);
				if ((cyclic & (1 << 6)) == 0) forced |= (1<<5);

				int natural = JPSNaturalStraightNeighbours;

				// Check if there are any forced neighbours which we can reach that are not natural neighbours
				//if ( ((forced & cyclic) & (~(natural & cyclic))) != 0 ) {
				if ((forced & (~natural) & cyclic) != 0) {
					// Some of the neighbour nodes are forced
					result = node;
					break;
				}

				// Make sure we can reach the next node
				if ((cyclic & (1 << forwardDir)) != 0) {
					node = nodes[node.nodeInGridIndex + forwardOffset];

					//Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta);
				} else {
					result = null;
					break;
				}
			}

			if (result == null) {
				while (origin != node) {
					origin.JPSDead[threadID] |= (byte)(1 << parentDir);
					origin = nodes[origin.nodeInGridIndex + forwardOffset];
				}
			} else {
				while (origin != node) {
					origin.JPSCache[parentDir + threadOffset] = result;
					origin = nodes[origin.nodeInGridIndex + forwardOffset];
				}
			}

			return result;
		}
示例#41
0
 private void ApplyGridGraphEndpointSpecialCase()
 {
     NavGraph[] graphs = AstarPath.active.graphs;
     for (int i = 0; i < graphs.Length; i++)
     {
         GridGraph gridGraph = graphs[i] as GridGraph;
         if (gridGraph != null)
         {
             GridNode[] nodes = gridGraph.nodes;
             int        num   = (gridGraph.neighbours != NumNeighbours.Four) ? ((gridGraph.neighbours != NumNeighbours.Eight) ? 6 : 8) : 4;
             for (int j = 0; j < gridGraph.depth; j++)
             {
                 for (int k = 0; k < gridGraph.width; k++)
                 {
                     GridNode gridNode = nodes[j * gridGraph.width + k];
                     if (!gridNode.Walkable)
                     {
                         int num2 = gridNode.NodeIndex * this.pivotCount;
                         for (int l = 0; l < this.pivotCount; l++)
                         {
                             this.costs[num2 + l] = uint.MaxValue;
                         }
                         for (int m = 0; m < num; m++)
                         {
                             int num3;
                             int num4;
                             if (gridGraph.neighbours == NumNeighbours.Six)
                             {
                                 num3 = k + gridGraph.neighbourXOffsets[GridGraph.hexagonNeighbourIndices[m]];
                                 num4 = j + gridGraph.neighbourZOffsets[GridGraph.hexagonNeighbourIndices[m]];
                             }
                             else
                             {
                                 num3 = k + gridGraph.neighbourXOffsets[m];
                                 num4 = j + gridGraph.neighbourZOffsets[m];
                             }
                             if (num3 >= 0 && num4 >= 0 && num3 < gridGraph.width && num4 < gridGraph.depth)
                             {
                                 GridNode gridNode2 = gridGraph.nodes[num4 * gridGraph.width + num3];
                                 if (gridNode2.Walkable)
                                 {
                                     for (int n = 0; n < this.pivotCount; n++)
                                     {
                                         uint val = this.costs[gridNode2.NodeIndex * this.pivotCount + n] + gridGraph.neighbourCosts[m];
                                         this.costs[num2 + n] = Math.Min(this.costs[num2 + n], val);
                                         Debug.DrawLine((Vector3)gridNode.position, (Vector3)gridNode2.position, Color.blue, 1f);
                                     }
                                 }
                             }
                         }
                         for (int num5 = 0; num5 < this.pivotCount; num5++)
                         {
                             if (this.costs[num2 + num5] == 4294967295u)
                             {
                                 this.costs[num2 + num5] = 0u;
                             }
                         }
                     }
                 }
             }
         }
     }
 }