/// <summary> /// Traverse neghbours and store a reference to it if possible. /// </summary> private void AddReferencesToNeighbours(Point[] neighbourPositions, SearchNode node) { for (int count = 0; count < neighbourPositions.Length; count++) { Point position = neighbourPositions[count]; // Check if the neighbour is part of the level. if ((position.X >= 0 && position.X <= levelWidth - 1) && (position.Y >= 0 && position.Y <= levelHeight - 1)) { SearchNode neighbour = searchNodes[position.X, position.Y]; // Add only walkable nodes. if (neighbour != null && neighbour.Walkable) { // Store a reference to the neighbour. node.Neighbours.Add(neighbour); } } } }
// <summary> /// Splits our level up into a grid of nodes. /// </summary> private void InitializeSearchNodes(Map map) { searchNodes = new SearchNode[levelWidth, levelHeight]; //For each of the tiles in our map, we // will create a search node for it. for (int x = 0; x < levelWidth; x++) { for (int y = 0; y < levelHeight; y++) { //Create a search node to represent this tile. SearchNode node = new SearchNode(); node.Position = new Point(x, y); // Our enemies can only walk on grass tiles. node.Walkable = map.GetIndex(x, y) == 0; // We only want to store nodes // that can be walked on. if (node.Walkable == true) { node.Neighbors = new SearchNode[4]; searchNodes[x, y] = node; } } } // Now for each of the search nodes, we will // connect it to each of its neighbours. for (int x = 0; x < levelWidth; x++) { for (int y = 0; y < levelHeight; y++) { SearchNode node = searchNodes[x, y]; // We only want to look at the nodes that // our enemies can walk on. if (node == null || node.Walkable == false) { continue; } // An array of all of the possible neighbors this // node could have. (We will ignore diagonals for now.) Point[] neighbors = new Point[] { new Point (x, y - 1), // The node above the current node new Point (x, y + 1), // The node below the current node. new Point (x - 1, y), // The node left of the current node. new Point (x + 1, y), // The node right of the current node }; // We loop through each of the possible neighbors for (int i = 0; i < neighbors.Length; i++) { Point position = neighbors[i]; // We need to make sure this neighbour is part of the level. if (position.X < 0 || position.X > levelWidth - 1 || position.Y < 0 || position.Y > levelHeight - 1) { continue; } SearchNode neighbor = searchNodes[position.X, position.Y]; // We will only bother keeping a reference // to the nodes that can be walked on. if (neighbor == null || neighbor.Walkable == false) { continue; } // Store a reference to the neighbor. node.Neighbors[i] = neighbor; } } } }
/// <summary> /// Use the parent field of the search nodes to trace /// a path from the end node to the start node. /// </summary> private List<Vector2> FindFinalPath(SearchNode startNode, SearchNode endNode) { closedList.Add(endNode); SearchNode parentTile = endNode.Parent; // Trace back through the nodes using the parent fields // to find the best path. while (parentTile != startNode) { closedList.Add(parentTile); parentTile = parentTile.Parent; } List<Vector2> finalPath = new List<Vector2>(); // Reverse the path and transform into world space. for (int i = closedList.Count - 1; i >= 0; i--) { finalPath.Add(new Vector2(closedList[i].Position.X * 32, closedList[i].Position.Y * 32)); } return finalPath; }
private void ProcessNeighbours(SearchNode activeNode, List<SearchNode> openList) { foreach (var node in activeNode.Neighbours) { if (node.Walkable && !node.InClosedList) { if (!node.InOpenList) { // Add neighbours to open list if it isn't already there openList.Add(node); node.InOpenList = true; // Mark active node as parent. node.SetParent(activeNode); // Set G value. node.G = activeNode.G + 10; } else { int tempG = activeNode.G + 10; // 10 IF THERE IS NO DIAGONAL if (node.G < 0 || node.G > tempG) { tempG = node.G; // Mark active node as parent. node.SetParent(activeNode); } } } } }
/// <summary> /// Use the parent field of the search nodes to trace /// a path from the end node to the start node. /// </summary> private Stack<Vector2> FindFinalPath(SearchNode startNode, SearchNode endNode, List<SearchNode> closeList) { closeList.Add(endNode); SearchNode parentTile = endNode.Parent; var finalPath = new Stack<Vector2>(); // Trace back through the nodes using the parent fields while (parentTile != startNode) { finalPath.Push(new Vector2(parentTile.Position.X * (int)Global.TileSize, parentTile.Position.Y * (int)Global.TileSize)); parentTile = parentTile.Parent; } // Reverse the path and transform into world space. finalPath.Reverse(); return finalPath; }
private SearchNode FindBestNode(List<SearchNode> openList) { SearchNode activeNode = new SearchNode(); // Finds next active node (minimal F value) int minF = int.MaxValue; foreach (var node in openList) { if (node.F < minF) { minF = node.F; activeNode = node; } } return activeNode; }
/// <summary> /// Creates node for each tile. /// </summary> private void CreateNodeForEachTile(Map map) { for (int coordX = 0; coordX < levelWidth; coordX++) { for (int coordY = 0; coordY < levelHeight; coordY++) { var node = new SearchNode(); node.Position = new Point(coordX, coordY); // Mark walkable terrain. 0 represent path on map grid. node.Walkable = map.GetValue(coordX, coordY) == WalkableLandValue; // Store only walkable nodes. if (node.Walkable) { node.Neighbours = new List<SearchNode>(); // TO BE CONSIDERED!!! searchNodes[coordX, coordY] = node; } } } }
/// <summary> /// Sets provided node as parent. /// </summary> public void SetParent(SearchNode parent) { this.Parent = parent; this.F = this.G + this.H; }
// <summary> /// Splits our level up into a grid of nodes. /// </summary> private void InitializeSearchNodes(Map map) { searchNodes = new SearchNode[levelWidth, levelHeight]; //For each of the tiles in our map, we // will create a search node for it. for (int x = 0; x < levelWidth; x++) { for (int y = 0; y < levelHeight; y++) { //Create a search node to represent this tile. SearchNode node = new SearchNode(); node.Position = new Point(x, y); // Our enemies can only walk on grass tiles. node.Walkable = map.GetIndex(x, y) == 0; // We only want to store nodes // that can be walked on. if (node.Walkable == true) { node.Neighbors = new SearchNode[4]; searchNodes[x, y] = node; } } } // Now for each of the search nodes, we will // connect it to each of its neighbours. for (int x = 0; x < levelWidth; x++) { for (int y = 0; y < levelHeight; y++) { SearchNode node = searchNodes[x, y]; // We only want to look at the nodes that // our enemies can walk on. if (node == null || node.Walkable == false) { continue; } // An array of all of the possible neighbors this // node could have. (We will ignore diagonals for now.) Point[] neighbors = new Point[] { new Point(x, y - 1), // The node above the current node new Point(x, y + 1), // The node below the current node. new Point(x - 1, y), // The node left of the current node. new Point(x + 1, y), // The node right of the current node }; // We loop through each of the possible neighbors for (int i = 0; i < neighbors.Length; i++) { Point position = neighbors[i]; // We need to make sure this neighbour is part of the level. if (position.X < 0 || position.X > levelWidth - 1 || position.Y < 0 || position.Y > levelHeight - 1) { continue; } SearchNode neighbor = searchNodes[position.X, position.Y]; // We will only bother keeping a reference // to the nodes that can be walked on. if (neighbor == null || neighbor.Walkable == false) { continue; } // Store a reference to the neighbor. node.Neighbors[i] = neighbor; } } } }
/// <summary> /// Finds the optimal path from one point to another. /// </summary> public List <Vector2> FindPath(Point startPoint, Point endPoint) { // Only try to find a path if the start and end points are different. if (startPoint == endPoint) { return(new List <Vector2>()); } ///////////////////////////////////////////////////////////////////// // Step 1 : Clear the Open and Closed Lists and reset each node’s F // and G values in case they are still set from the last // time we tried to find a path. ///////////////////////////////////////////////////////////////////// ResetSearchNodes(); // Store references to the start and end nodes for convenience. SearchNode startNode = searchNodes[startPoint.X, startPoint.Y]; SearchNode endNode = searchNodes[endPoint.X, endPoint.Y]; ///////////////////////////////////////////////////////////////////// // Step 2 : Set the start node’s G value to 0 and its F value to the // estimated distance between the start node and goal node // (this is where our H function comes in) and add it to the // Open List. ///////////////////////////////////////////////////////////////////// startNode.InOpenList = true; startNode.DistanceToGoal = Heuristic(startPoint, endPoint); startNode.DistanceTraveled = 0; openList.Add(startNode); ///////////////////////////////////////////////////////////////////// // Setp 3 : While there are still nodes to look at in the Open list : ///////////////////////////////////////////////////////////////////// while (openList.Count > 0) { ///////////////////////////////////////////////////////////////// // a) : Loop through the Open List and find the node that // has the smallest F value. ///////////////////////////////////////////////////////////////// SearchNode currentNode = FindBestNode(); ///////////////////////////////////////////////////////////////// // b) : If the Open List empty or no node can be found, // no path can be found so the algorithm terminates. ///////////////////////////////////////////////////////////////// if (currentNode == null) { break; } ///////////////////////////////////////////////////////////////// // c) : If the Active Node is the goal node, we will // find and return the final path. ///////////////////////////////////////////////////////////////// if (currentNode == endNode) { // Trace our path back to the start. return(FindFinalPath(startNode, endNode)); } ///////////////////////////////////////////////////////////////// // d) : Else, for each of the Active Node’s neighbours : ///////////////////////////////////////////////////////////////// for (int i = 0; i < currentNode.Neighbors.Length; i++) { SearchNode neighbor = currentNode.Neighbors[i]; ////////////////////////////////////////////////// // i) : Make sure that the neighbouring node can // be walked across. ////////////////////////////////////////////////// if (neighbor == null || neighbor.Walkable == false) { continue; } ////////////////////////////////////////////////// // ii) Calculate a new G value for the neighbouring node. ////////////////////////////////////////////////// float distanceTraveled = currentNode.DistanceTraveled + 1; // An estimate of the distance from this node to the end node. float heuristic = Heuristic(neighbor.Position, endPoint); ////////////////////////////////////////////////// // iii) If the neighbouring node is not in either the Open // List or the Closed List : ////////////////////////////////////////////////// if (neighbor.InOpenList == false && neighbor.InClosedList == false) { // (1) Set the neighbouring node’s G value to the G value we just calculated. neighbor.DistanceTraveled = distanceTraveled; // (2) Set the neighbouring node’s F value to the new G value + the estimated // distance between the neighbouring node and goal node. neighbor.DistanceToGoal = distanceTraveled + heuristic; // (3) Set the neighbouring node’s Parent property to point at the Active Node. neighbor.Parent = currentNode; // (4) Add the neighbouring node to the Open List. neighbor.InOpenList = true; openList.Add(neighbor); } ////////////////////////////////////////////////// // iv) Else if the neighbouring node is in either the Open // List or the Closed List : ////////////////////////////////////////////////// else if (neighbor.InOpenList || neighbor.InClosedList) { // (1) If our new G value is less than the neighbouring // node’s G value, we basically do exactly the same // steps as if the nodes are not in the Open and // Closed Lists except we do not need to add this node // the Open List again. if (neighbor.DistanceTraveled > distanceTraveled) { neighbor.DistanceTraveled = distanceTraveled; neighbor.DistanceToGoal = distanceTraveled + heuristic; neighbor.Parent = currentNode; } } } ///////////////////////////////////////////////////////////////// // e) Remove the Active Node from the Open List and add it to the // Closed List ///////////////////////////////////////////////////////////////// openList.Remove(currentNode); currentNode.InClosedList = true; } // No path could be found. return(new List <Vector2>()); }