/// <summary> /// If neighbour has line of sight to currents parent and skipping current is cheaper, skips current node and sets neighbour cost / parent depending on whether current is used /// </summary> /// <param name="current"></param> /// <param name="neighbour"></param> /// <param name="settings"></param> /// <param name="openNodes"></param> private static void UpdateNode(Node current, Node neighbour, PathfindingSettings settings, BucketList <Node> openNodes) { if (LineOfSight(current.parent, neighbour)) { var costSkippingCurrent = current.parent.cost + settings.CostIncrease(current.parent.pos, neighbour.pos); if (costSkippingCurrent < neighbour.cost) { if (openNodes.Contains(neighbour)) { openNodes.Remove(neighbour); } neighbour.cost = costSkippingCurrent; neighbour.parent = current.parent; openNodes.Add(neighbour); } } else { var costUsingCurrent = current.cost + settings.CostIncrease(current.pos, neighbour.pos); if (costUsingCurrent < neighbour.cost) { if (openNodes.Contains(neighbour)) { openNodes.Remove(neighbour); } neighbour.cost = costUsingCurrent; neighbour.parent = current; openNodes.Add(neighbour); } } }
/// <summary> /// Loads a level from file and initializes the world from its data then returns the level name. /// </summary> /// <param name="_loadFile">Full file path to level xml save file.</param> public string Load(string _loadFile) { //Debug.Log("Loading level from file: " + _loadFile); var levelName = "unknown"; Current.Clear(); using (var xmlReader = XmlReader.Create(_loadFile)) { while (xmlReader.Read()) { if (xmlReader.IsStartElement()) { switch (xmlReader.Name) { case "LevelName": xmlReader.Read(); levelName = xmlReader.Value; break; case "LevelPlayerSpawn": var px = int.Parse(xmlReader["X"]); var py = int.Parse(xmlReader["Y"]); GameObject.FindGameObjectWithTag("PlayerSpawn").transform.position = new Vector2(px, py); break; case "LevelAISpawn": var aiX = int.Parse(xmlReader["X"]); var aiY = int.Parse(xmlReader["Y"]); GameObject.FindGameObjectWithTag("AISpawn").transform.position = new Vector2(aiX, aiY); break; case "PathfindingSettings": PathfindingSettings.SetHeuristicFromID(int.Parse(xmlReader["Heuristic"])); PathfindingSettings.FallJumpLinkMaxDist = int.Parse(xmlReader["FallJumpLinkDistance"]); break; case "Tile": var x = int.Parse(xmlReader["TileX"]); var y = int.Parse(xmlReader["TileY"]); var type = Tile.GetTypeFromString(xmlReader["TileType"]); Current.Tiles[x, y].Type = type; break; } } } } PlatformCount = GetTileCountOfType(TileType.Platform); OnWorldModifyFinishCallback(); //Debug.Log("Finished loading level: " + levelName); return(levelName); }
public static AlgorithmSolution Find(PathfindingSettings settings, int mapWidth, int mapHeight, IEnumerable <Point> obstacles) { var observer = new AlgorithmObserverFactory(); var start = new Point(settings.FromX, settings.FromY); var goal = new Point(settings.GoalX, settings.GoalY); var boundary = new Rectangle(0, 0, mapWidth, mapHeight); var unit = 1; var queryable = HeuristicSearch.Use(settings.Algorithm, start, goal, (step, i) => step.GetFourDirections(unit), null, observer); var solution = ApplyHeuristicFunction(queryable.Except(obstacles).Where(boundary.Contains), settings.Heuristics); return(new AlgorithmSolution() { Details = observer.Details, Solution = solution.ToArray(), NumberOfEstimatedNodes = observer.Estimated.Count, }); }
public ResponseBody <AlgorithmSolution> Find([FromBody] PathfindingRequestBody body) { var settings = body.ToSettings(); if (!PathfindingSettings.CheckIfValid(settings)) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return(new ResponseBody <AlgorithmSolution>() { Errors = new[] { ResponseError.InvalidFromAndGoalSettings } }); } try { var result = AlgorithmCore.Find(settings, body.Map); return(new ResponseBody <AlgorithmSolution>() { Data = result }); } catch (InvalidOperationException) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return(new ResponseBody <AlgorithmSolution>() { }); } catch (Exception) { Response.StatusCode = (int)HttpStatusCode.InternalServerError; return(new ResponseBody <AlgorithmSolution>() { }); } }
/// <summary> /// Tries to find a path from start to goal node using settings /// </summary> /// <param name="start">Start node</param> /// <param name="goal">Goal node</param> /// <param name="settings">PathfindingSettings (which heuristics to use etc)</param> /// <param name="isoLevel">Determines which nodes are considered walkable (iso value > this)</param> /// <param name="openNodes">Nodes that were still open after algorithm finished</param> /// <param name="closedNodes">Nodes that were fully explored after algorithm finished</param> /// <param name="maxIterations">Max number of loop iterations, prevents infinite loops</param> /// <param name="nodeCount">Approximate total node count, determines start capacity of path stack</param> /// <returns></returns> public static Stack <Vector3> FindPath(Node start, Node goal, PathfindingSettings settings, float isoLevel, out BucketList <Node> openNodes, out HashSet <Node> closedNodes, int maxIterations = 50000, int nodeCount = 1000) { NodesGenerator.CostHeuristicBalance = settings.greediness; int neighbourChecks = 0; int numIterations = 0; //euclidean distance from start to end float distance = Vector3.Distance(start.pos, goal.pos); //full length of path float pathLength = 0; System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); if (settings.benchmark) { sw.Start(); } var startCapacity = nodeCount / 100; Stack <Vector3> path = new Stack <Vector3>(startCapacity); openNodes = new BucketList <Node>(distance / 100, Mathf.Min(settings.Heuristic(start.pos, goal.pos) * settings.greediness, settings.CostIncrease(start.pos, goal.pos) * (1 - settings.greediness))); //openNodes = new MinHeap<Node>(startCapacity * 10); closedNodes = new HashSet <Node>(); Node current = null; start.cost = 0; start.parent = start; start.heuristic = settings.Heuristic(start.pos, goal.pos); openNodes.Add(start); while (openNodes.Count != 0 && !closedNodes.Contains(goal)) { if (++numIterations == maxIterations) { break; } current = openNodes.ExtractMin(); closedNodes.Add(current); foreach (var neighbour in current.neighbours) { neighbourChecks++; if (neighbour.isoValue <= isoLevel) { continue; } if (closedNodes.Contains(neighbour)) { continue; } var newCost = current.cost + settings.CostIncrease(current.pos, neighbour.pos); if (openNodes.Contains(neighbour)) { if (newCost >= neighbour.cost) { continue; } openNodes.Remove(neighbour); } neighbour.parent = current; neighbour.heuristic = settings.Heuristic(neighbour.pos, goal.pos); neighbour.cost = newCost; openNodes.Add(neighbour); } } if (!closedNodes.Contains(goal)) { Debug.Log("no goal, " + numIterations + " iterations, closed: " + closedNodes.Count + ", opened: " + openNodes.Count); return(path); } path.Push(goal.pos); Node temp = goal.parent; while (temp != null) { pathLength += Vector3.Distance(path.Peek(), temp.pos); path.Push(temp.pos); if (temp == start) { break; } temp = temp.parent; } if (settings.benchmark) { sw.Stop(); Debug.Log("A*, Heuristic: " + settings.heuristic + ", Cost increase: " + settings.costIncrease + ", Path length: " + pathLength * 100 / distance + "%, ms: " + sw.Elapsed.TotalMilliseconds + ", closed: " + closedNodes.Count + ", opened: " + openNodes.Count + ", Neighbour checks: " + neighbourChecks); } return(path); }
public static AlgorithmSolution Find(PathfindingSettings settings, int[][] map) { return(Find(settings, map.Max(row => row.Length), map.Length, GetAllObstacles(map))); }