/// <summary> /// Attempts to find a <see cref="Path"/> between the start and end points and returns the result on completion. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="diagonal">The diagonal mode used when finding paths</param> /// <returns>The <see cref="Path"/> that was found or null if the algorithm failed</returns> public Path findPathImmediate(Index start, Index end, DiagonalMode diagonal) { PathRequestStatus status = PathRequestStatus.InvalidIndex; // Call through return(findPathImmediate(start, end, out status, diagonal)); }
/// <summary> /// Launches the pathfinding algorithm and attempts to find a <see cref="Path"/> between the start and end <see cref="Index"/>. /// This overload accepts a <see cref="MonoDelegate"/> as a callback. /// </summary> /// <param name="start">The start position for the search</param> /// <param name="end">The end position for the search</param> /// <param name="diagonal">The diagonal mod used when finding paths</param> /// <param name="callback">The <see cref="MonoDelegate"/> to invoke on completion</param> public void findPath(Index start, Index end, DiagonalMode diagonal, MonoDelegate callback) { // Call through findPath(start, end, diagonal, (Path path, PathRequestStatus status) => { // Invoke the mono delegate callback.invoke(new MonoDelegateEvent(path, status)); }); }
public AsyncPathRequest(SearchGrid grid, Index start, Index end, DiagonalMode diagonal, PathRequestDelegate callback) { this.grid = grid; this.start = start; this.end = end; this.diagonal = diagonal; this.callback = callback; // Create a time stamp timeStamp = DateTime.UtcNow.Ticks; }
/// <summary> /// Attempts to search this grid for a path between the start and end points. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="diagonal">The diagonal mode used when finding paths</param> /// <param name="callback">The <see cref="PathRequestDelegate"/> method to call whe the algorithm has completed</param> public void findPath(Index start, Index end, DiagonalMode diagonal, PathRequestDelegate callback) { // Make sure the grid is ready if (verifyReady() == false) { callback(null, PathRequestStatus.GridNotReady); return; } // Update max path length searchGrid.maxPathLength = maxPathLength; // Get the threading value bool useThreading = allowThreading; #if UNITY_WEBGL // Threading is not allowed on web gl platform useThreading = false; #endif // Check if threading is enabled if (useThreading == true) { // Create a request AsyncPathRequest request = new AsyncPathRequest(searchGrid, start, end, diagonal, (Path path, PathRequestStatus status) => { #if UNITY_EDITOR // Pass the path for rendering before it is used by the caller otherwise nodes may be removed from the path PathView.setRenderPath(this, path); #endif // Invoke callback callback(path, status); }); // Dispatch the request ThreadManager.Active.asyncRequest(request); } else { PathRequestStatus status; // Run the task immediatley Path result = findPathImmediate(start, end, out status, diagonal); #if UNITY_EDITOR // Pass the path for rendering before it is used by the caller otherwise nodes may be removed PathView.setRenderPath(this, result); #endif // Trigger callback callback(result, status); } }
public AsyncPathRequest(SearchGrid grid, Index start, Index end, DiagonalMode diagonal, BaseTraversal2D traversal2D, PathRequestDelegate callback) { this.Grid = grid; this.Start = start; this.End = end; this.Diagonal = diagonal; this.Callback = callback; this.Traversal2D = traversal2D; // Create a time stamp TimeStamp = DateTime.UtcNow.Ticks; }
public void FindPath(Index start, Index end, DiagonalMode diagonal, BaseTraversal2D traversal2D, PathRequestDelegate callback) { if (!IsValid(start) || !IsValid(end)) { return; } searchGrid.maxPathLength = maxPathLength; bool useThreading = allowThreading; if (useThreading == true) { AsyncPathRequest request = new AsyncPathRequest(searchGrid, start, end, diagonal, traversal2D, (Path path, PathRequestStatus status) => { PathView.setRenderPath(this, path); callback(path, status); }); ThreadManager.Active.asyncRequest(request); } else { PathRequestStatus status; Path result = FindPathImmediate(start, end, out status, diagonal); PathView.setRenderPath(this, result); callback(result, status); } Path FindPathImmediate(Index subStart, Index subEnd, out PathRequestStatus subStatus, DiagonalMode subDiagonal) { searchGrid.maxPathLength = maxPathLength; Path path = null; PathRequestStatus temp = PathRequestStatus.InvalidIndex; searchGrid.FindPath(subStart, subEnd, subDiagonal, traversal2D, (Path result, PathRequestStatus resultStatus) => { temp = resultStatus; if (resultStatus == PathRequestStatus.PathFound) { path = result; PathView.setRenderPath(this, path); } }); subStatus = temp; return(path); } }
//获得节点之间的距离 public int GetDistance(Index start, Index end, DiagonalMode diagonal) { int count = 1; int deltaX = Mathf.Abs(start.X - end.X); int deltaY = Mathf.Abs(start.Y - end.Y); if (diagonal == DiagonalMode.NoDiagonal) { count += (deltaX + deltaY); } else { int smallest = deltaX; if (deltaY < smallest) { smallest = deltaY; } int diagonalSteps = Mathf.Abs(deltaX - deltaY); count += (diagonalSteps + smallest); } return(count); }
/// <summary> /// Calculates the minumum number of node steps required tbetween the start and end nodes. /// This will only ever provide the best possible case and will not take into account non-walkable nodes or obstructions. /// </summary> /// <param name="start">The start index</param> /// <param name="end">The end index</param> /// <param name="diagonal">The diagonal mode to use</param> /// <returns>The number of nodes required to move between the start and end indexes</returns> public static int findNodeDistance(Index start, Index end, DiagonalMode diagonal) { // Include start node int count = 1; // Change in x int deltaX = Mathf.Abs(start.X - end.X); // Change in y int deltaY = Mathf.Abs(start.Y - end.Y); if (diagonal == DiagonalMode.NoDiagonal) { // Add the x and y offset count += (deltaX + deltaY); } else { // Get the smallest delta int smallest = deltaX; // Check if y is smaller if (deltaY < smallest) { smallest = deltaY; } // Get the number of diagonal steps int diagonalSteps = Mathf.Abs(deltaX - deltaY); // Add the diagonal offset count += (diagonalSteps + smallest); } return(count); }
/// <summary> /// Attempts to find a <see cref="Path"/> between the start and end points and returns the result on completion. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="diagonal">The diagonal mode used when finding paths</param> /// <returns>The <see cref="Path"/> that was found or null if the algorithm failed</returns> public abstract Path findPathImmediate(Index start, Index end, DiagonalMode diagonal);
/// <summary> /// Attempts to find a <see cref="Path"/> between the start and end points and returns the result on completion. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="status">The <see cref="PathRequestStatus"/> describing the state of the result</param> /// <param name="diagonal">The diagonal mode used when finding paths</param> /// <returns>The <see cref="Path"/> that was found or null if the algorithm failed</returns> public abstract Path findPathImmediate(Index start, Index end, out PathRequestStatus status, DiagonalMode diagonal);
private int constructAdjacentNodes(PathNode center, PathNode[] nodes, DiagonalMode diagonal) { // Get the center node Index node = center.Index; // Clear the shared array so that old data is not used for (int i = 0; i < nodes.Length; i++) { nodes[i] = null; } int index = 0; // Check for per node diagonal status if (center.DiagonalMode != PathNodeDiagonalMode.UseGlobal) { switch (center.DiagonalMode) { case PathNodeDiagonalMode.Diagonal: diagonal = DiagonalMode.Diagonal; break; case PathNodeDiagonalMode.NoDiagonal: diagonal = DiagonalMode.NoDiagonal; break; case PathNodeDiagonalMode.DiagonalNoCutting: diagonal = DiagonalMode.DiagonalNoCutting; break; } } // Check if diagonal movements can be used if (diagonal != DiagonalMode.NoDiagonal) { // Cache the adjacent nodes PathNode left = safeGetNode(node.X - 1, node.Y); PathNode right = safeGetNode(node.X + 1, node.Y); PathNode top = safeGetNode(node.X, node.Y + 1); PathNode bottom = safeGetNode(node.X, node.Y - 1); bool canAdd = true; // Bottom left { canAdd = true; if (diagonal == DiagonalMode.DiagonalNoCutting) { // Left cutting if (left != null && left.IsWalkable == false) { canAdd = false; } // Bottom cutting if (bottom != null && bottom.IsWalkable == false) { canAdd = false; } } // Make sure the diagonal movement is allowed if (canAdd == true) { nodes[index++] = safeGetNode(node.X - 1, node.Y - 1); } } // End bottom left // Top right { canAdd = true; if (diagonal == DiagonalMode.DiagonalNoCutting) { // Right cutting if (right != null && right.IsWalkable == false) { canAdd = false; } // Top cutting if (top != null && top.IsWalkable == false) { canAdd = false; } } // Make sure the diagonal movement is allowed if (canAdd == true) { nodes[index++] = safeGetNode(node.X + 1, node.Y + 1); } } // End top right // Top Left { canAdd = true; if (diagonal == DiagonalMode.DiagonalNoCutting) { // Left cutting if (left != null && left.IsWalkable == false) { canAdd = false; } // Top cutting if (top != null && top.IsWalkable == false) { canAdd = false; } } // Make sure the diagonal movement is allowed if (canAdd == true) { nodes[index++] = safeGetNode(node.X - 1, node.Y + 1); } } // End top left // Bottom right { canAdd = true; if (diagonal == DiagonalMode.DiagonalNoCutting) { // Right cutting if (right != null && right.IsWalkable == false) { canAdd = false; } // Bottom cutting if (bottom != null && bottom.IsWalkable == false) { canAdd = false; } } // Make sure the diagonal movement is allowed if (canAdd == true) { nodes[index++] = safeGetNode(node.X + 1, node.Y - 1); } } // End bottom right } // Bottom nodes[index++] = safeGetNode(node.X, node.Y - 1); // Left nodes[index++] = safeGetNode(node.X - 1, node.Y); // Right nodes[index++] = safeGetNode(node.X + 1, node.Y); // Top nodes[index++] = safeGetNode(node.X, node.Y + 1); return(index + 1); }
/// <summary> /// Attempts to search this grid for a path between the start and end points. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="diagonal">The diagonal mode used when finding paths</param> /// <param name="callback">The <see cref="PathRequestDelegate"/> method to call whe the algorithm has completed</param> public abstract void findPath(Index start, Index end, DiagonalMode diagonal, PathRequestDelegate callback);
/// <summary> /// Launches the pathfinding algorithm and attempts to find a <see cref="Path"/> between the start and end <see cref="Index"/>. /// </summary> /// <param name="start">The start position for the search</param> /// <param name="end">The end position for the search</param> /// <param name="diagonal">The diagonal mod used when finding paths</param> /// <param name="callback">The <see cref="PathRequestDelegate"/> method to call on completion</param> public void findPath(Index start, Index end, DiagonalMode diagonal, PathRequestDelegate callback) { // Already at the destination if (start.Equals(end)) { callback(null, PathRequestStatus.SameStartEnd); return; } // Get the nodes PathNode startNode = nodeGrid[start.X, start.Y]; PathNode endNode = nodeGrid[end.X, end.Y]; // Clear all previous data clearSearchData(); // Starting scores startNode.g = 0; startNode.h = provider.heuristic(startNode, endNode); startNode.f = startNode.h; // Add the start node openMap.add(startNode); runtimeMap.add(startNode); orderedMap.push(startNode); while (openMap.Count > 0) { // Get the front value PathNode value = orderedMap.pop(); if (value == endNode) { // We have found the path Path result = constructPath(searchGrid[endNode.Index.X, endNode.Index.Y]); // Last node if (maxPathLength == -1 || result.NodeCount < maxPathLength) { result.push(endNode, endNode.Index); } // Trigger the delegate with success callback(result, PathRequestStatus.PathFound); // Exit the method return; } else { openMap.remove(value); closedMap.add(value); // Fill our array with surrounding nodes constructAdjacentNodes(value, adjacentNodes, diagonal); // Process each neighbor foreach (PathNode adjacent in adjacentNodes) { bool isBetter = false; // Skip null nodes if (adjacent == null) { continue; } // Make sure the node is walkable if (adjacent.IsWalkable == false) { continue; } // Check for occupied if (CheckIndexOccupied != null) { if (CheckIndexOccupied(adjacent.Index) == true) { continue; } } // Make sure it has not already been excluded if (closedMap.contains(adjacent) == true) { continue; } // Check for custom exclusion descisions if (validateConnection(value, adjacent) == false) { continue; } // Calculate the score for the node float score = runtimeMap[value].g + provider.adjacentDistance(value, adjacent) + (adjacent.Weighting * weightingInfluence); bool added = false; // Make sure it can be added to the open map if (openMap.contains(adjacent) == false) { openMap.add(adjacent); isBetter = true; added = true; } else if (score < runtimeMap[adjacent].g) { // The score is better isBetter = true; } else { // The score is not better isBetter = false; } // CHeck if a better score has been found if (isBetter == true) { // Update the search grid searchGrid[adjacent.Index.X, adjacent.Index.Y] = value; // Add the adjacent node if (runtimeMap.contains(adjacent) == false) { runtimeMap.add(adjacent); } // Update the score values for the node runtimeMap[adjacent].g = score; runtimeMap[adjacent].h = provider.heuristic(adjacent, endNode); runtimeMap[adjacent].f = runtimeMap[adjacent].g + runtimeMap[adjacent].h; // CHeck if we added to the open map if (added == true) { // Push the adjacent node to the set orderedMap.push(adjacent); } else { // Refresh the set orderedMap.refresh(adjacent); } } } } } // End while // Failure callback(null, PathRequestStatus.PathNotFound); }
/// <summary> /// Attempts to find a <see cref="Path"/> between the start and end points and returns the result on completion. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="status">The <see cref="PathRequestStatus"/> describing the state of the result</param> /// <param name="diagonal">The diagonal mode used when finding a path</param> /// <returns>The <see cref="Path"/> that was found or null if the algorithm failed</returns> public Path findPathImmediate(Index start, Index end, out PathRequestStatus status, DiagonalMode diagonal) { // Make sure the grid is ready if (verifyReady() == false) { status = PathRequestStatus.GridNotReady; return(null); } // Update max path length searchGrid.maxPathLength = maxPathLength; // Store a temp path Path path = null; PathRequestStatus temp = PathRequestStatus.InvalidIndex; // Find a path searchGrid.findPath(start, end, diagonal, (Path result, PathRequestStatus resultStatus) => { // Store the status temp = resultStatus; // Make sure the path was found if (resultStatus == PathRequestStatus.PathFound) { path = result; #if UNITY_EDITOR PathView.setRenderPath(this, path); #endif } }); status = temp; return(path); }
/// <summary> /// Attempts to search this grid for a path between the start and end points. /// </summary> /// <param name="start">The <see cref="Index"/> into the search space representing the start position</param> /// <param name="end">The <see cref="Index"/> into the search space representing the end position</param> /// <param name="diagonal">The diagonal mode used when finding paths</param> /// <param name="callback">The <see cref="PathRequestDelegate"/> method to call whe the algorithm has completed</param> public abstract void findPath(Index start, Index end, DiagonalMode diagonal, Action <Path, PathRequestStatus> callback);
private IPathNode[] getSurroundingNodes(int x, int y) { // Create the array IPathNode[] nodes = new IPathNode[(visualizeGrid.diagonalMovement == DiagonalMode.NoDiagonal) ? 4 : 8]; // Center node IPathNode center = safeGetNode(x, y); // Left node nodes[0] = safeGetNode(x - 1, y); // Right node nodes[1] = safeGetNode(x + 1, y); // Up node nodes[2] = safeGetNode(x, y + 1); // Down node nodes[3] = safeGetNode(x, y - 1); DiagonalMode diagonalMode = visualizeGrid.diagonalMovement; if (center.DiagonalMode != PathNodeDiagonalMode.UseGlobal) { switch (center.DiagonalMode) { case PathNodeDiagonalMode.Diagonal: diagonalMode = DiagonalMode.Diagonal; break; case PathNodeDiagonalMode.NoDiagonal: diagonalMode = DiagonalMode.NoDiagonal; break; case PathNodeDiagonalMode.DiagonalNoCutting: diagonalMode = DiagonalMode.DiagonalNoCutting; break; } } // Diagonal neighbors if (diagonalMode != DiagonalMode.NoDiagonal) { IPathNode left = safeGetNode(x - 1, y); IPathNode right = safeGetNode(x + 1, y); IPathNode top = safeGetNode(x, y + 1); IPathNode bottom = safeGetNode(x, y - 1); bool canAdd = true; // Top left if (diagonalMode == DiagonalMode.DiagonalNoCutting) { if (left != null && left.IsWalkable == false) { canAdd = false; } if (top != null && top.IsWalkable == false) { canAdd = false; } } if (canAdd == true) { nodes[4] = safeGetNode(x - 1, y + 1); } // Top right canAdd = true; if (diagonalMode == DiagonalMode.DiagonalNoCutting) { if (right != null && right.IsWalkable == false) { canAdd = false; } if (top != null && top.IsWalkable == false) { canAdd = false; } } if (canAdd == true) { nodes[5] = safeGetNode(x + 1, y + 1); } // Bottom left canAdd = true; if (diagonalMode == DiagonalMode.DiagonalNoCutting) { if (left != null && left.IsWalkable == false) { canAdd = false; } if (bottom != null && bottom.IsWalkable == false) { canAdd = false; } } if (canAdd == true) { nodes[6] = safeGetNode(x - 1, y - 1); } // Bottom right canAdd = true; if (diagonalMode == DiagonalMode.DiagonalNoCutting) { if (right != null && right.IsWalkable == false) { canAdd = false; } if (bottom != null && bottom.IsWalkable == false) { canAdd = false; } } if (canAdd == true) { nodes[7] = safeGetNode(x + 1, y - 1); } } return(nodes); }
public void FindPath(Index start, Index end, DiagonalMode diagonal, BaseTraversal2D traversal, PathRequestDelegate callback) { // Already at the destination if (start.Equals(end)) { callback(null, PathRequestStatus.SameStartEnd); return; } // Get the nodes PathNode startNode = nodeGrid[start.X, start.Y]; PathNode endNode = nodeGrid[end.X, end.Y]; // Clear all previous data ClearSearchData(); // Starting scores startNode.g = 0; startNode.h = provider.heuristic(startNode, endNode); startNode.f = startNode.h; // Add the start node openMap.add(startNode); runtimeMap.add(startNode); orderedMap.Push(startNode); while (openMap.Count > 0) { // Get the front value PathNode value = orderedMap.Pop(); if (value == endNode) { // We have found the path Path result = ConstructPath(searchGrid[endNode.Index.X, endNode.Index.Y]); // Last node if (maxPathLength == -1 || result.NodeCount < maxPathLength) { result.Push(endNode); } // Trigger the delegate with success callback(result, PathRequestStatus.PathFound); // Exit the method return; } else { openMap.remove(value); closedMap.add(value); // Fill our array with surrounding nodes ConstructAdjacentNodes(value, adjacentNodes, diagonal); // Process each neighbor foreach (PathNode pathNode in adjacentNodes) { bool isBetter = false; // Skip null nodes if (pathNode == null) { continue; } // Make sure the node is walkable if (pathNode.IsWalkable == false) { continue; } // Check for occupied if (IsIndexObstacle != null) { if (IsIndexObstacle(pathNode.Index) == true) { continue; } } //检查Traversal if (traversal != null) { if (traversal.Filter(pathNode.TileNode) == false) { continue; } } // Make sure it has not already been excluded if (closedMap.contains(pathNode) == true) { continue; } // Check for custom exclusion descisions if (ValidateConnection(value, pathNode) == false) { continue; } // Calculate the score for the node float score = runtimeMap[value].g + provider.adjacentDistance(value, pathNode) + (pathNode.Weighting * weightingInfluence); bool added = false; // Make sure it can be added to the open map if (openMap.contains(pathNode) == false) { openMap.add(pathNode); isBetter = true; added = true; } else if (score < runtimeMap[pathNode].g) { // The score is better isBetter = true; } else { // The score is not better isBetter = false; } // CHeck if a better score has been found if (isBetter == true) { // Update the search grid searchGrid[pathNode.Index.X, pathNode.Index.Y] = value; // Add the adjacent node if (runtimeMap.contains(pathNode) == false) { runtimeMap.add(pathNode); } // Update the score values for the node runtimeMap[pathNode].g = score; runtimeMap[pathNode].h = provider.heuristic(pathNode, endNode); runtimeMap[pathNode].f = runtimeMap[pathNode].g + runtimeMap[pathNode].h; // CHeck if we added to the open map if (added == true) { // Push the adjacent node to the set orderedMap.Push(pathNode); } else { // Refresh the set orderedMap.Refresh(pathNode); } } } } } // End while // Failure callback(null, PathRequestStatus.PathNotFound); void ClearSearchData() { // Reset all data closedMap.clear(); openMap.clear(); runtimeMap.clear(); orderedMap.Clear(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { searchGrid[x, y] = null; } } } //构造路径 Path ConstructPath(PathNode current) { // Create the path Path path = new Path(this); // Call the dee construct method DeepConstructPath(current, path); return(path); } void DeepConstructPath(PathNode inputCurrent, Path output) { // Get the node from the search grid PathNode node = searchGrid[inputCurrent.Index.X, inputCurrent.Index.Y]; // Make sure we have a valid node if (node != null) { // Call through reccursive DeepConstructPath(node, output); } // Limit the maximumnumber of nodes in the path if (maxPathLength != -1) { if (output.NodeCount > maxPathLength) { return; } } // Push the node to the path output.Push(inputCurrent); } }