public PathfindingResult Run(int startX, int startY, int endX, int endY, TileProvider provider, out List <PNode> path) { if (provider == null) { path = null; return(PathfindingResult.ERROR_INTERNAL); } // Validate start and end points. if (!provider.TileInBounds(startX, startY)) { path = null; return(PathfindingResult.ERROR_START_OUT_OF_BOUNDS); } if (!provider.TileInBounds(endX, endY)) { path = null; return(PathfindingResult.ERROR_END_OUT_OF_BOUNDS); } if (!provider.IsTileWalkable(startX, startY)) { path = null; return(PathfindingResult.ERROR_START_NOT_WALKABLE); } if (!provider.IsTileWalkable(endX, endY)) { path = null; return(PathfindingResult.ERROR_END_NOT_WALKABLE); } // Clear everything up. Clear(); var start = PNode.Create(startX, startY); var end = PNode.Create(endX, endY); // Check the start/end relationship. if (start.Equals(end)) { path = null; return(PathfindingResult.ERROR_START_IS_END); } // Add the starting point to all relevant structures. open.Enqueue(start, 0f); cameFrom[start] = start; costSoFar[start] = 0f; int count; while ((count = open.Count) > 0) { // Detect if the current open amount exceeds the capacity. // This only happens in very large open areas. Corridors and hallways will never cause this, not matter how large the actual path length. if (count >= MAX - 8) { path = null; return(PathfindingResult.ERROR_PATH_TOO_LONG); } var current = open.Dequeue(); if (current.Equals(end)) { // We found the end of the path! path = TracePath(end); return(PathfindingResult.SUCCESSFUL); } // Get all neighbours (tiles that can be walked on to) var neighbours = GetNear(current, provider); foreach (PNode n in neighbours) { float newCost = costSoFar[current] + GetCost(current, n); // Note that this could change depending on speed changes per-tile. if (!costSoFar.ContainsKey(n) || newCost < costSoFar[n]) { costSoFar[n] = newCost; float priority = newCost + Heuristic(current, n); open.Enqueue(n, priority); cameFrom[n] = current; } } } path = null; return(PathfindingResult.ERROR_INTERNAL); }
private List <PNode> GetNear(PNode node, TileProvider provider) { // Want to add nodes connected to the center node, if they are walkable. // This code stops the pathfinder from cutting corners, and going through walls that are diagonal from each other. near.Clear(); // Left left = false; if (provider.IsTileWalkable(node.X - 1, node.Y)) { near.Add(PNode.Create(node.X - 1, node.Y)); left = true; } // Right right = false; if (provider.IsTileWalkable(node.X + 1, node.Y)) { near.Add(PNode.Create(node.X + 1, node.Y)); right = true; } // Above above = false; if (provider.IsTileWalkable(node.X, node.Y + 1)) { near.Add(PNode.Create(node.X, node.Y + 1)); above = true; } // Below below = false; if (provider.IsTileWalkable(node.X, node.Y - 1)) { near.Add(PNode.Create(node.X, node.Y - 1)); below = true; } // Above-Left if (left && above) { if (provider.IsTileWalkable(node.X - 1, node.Y + 1)) { near.Add(PNode.Create(node.X - 1, node.Y + 1)); } } // Above-Right if (right && above) { if (provider.IsTileWalkable(node.X + 1, node.Y + 1)) { near.Add(PNode.Create(node.X + 1, node.Y + 1)); } } // Below-Left if (left && below) { if (provider.IsTileWalkable(node.X - 1, node.Y - 1)) { near.Add(PNode.Create(node.X - 1, node.Y - 1)); } } // Below-Right if (right && below) { if (provider.IsTileWalkable(node.X + 1, node.Y - 1)) { near.Add(PNode.Create(node.X + 1, node.Y - 1)); } } return(near); }