using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Gley.UrbanSystem.Internal { internal class AStar { /// /// Find a path between 2 waypoints. /// Uses A* algorithm. /// internal List FindPath(int startNodeIndex, int targetNodeIndex, int pedestraianTypes, PathFindingWaypoint[] allPathFindingWaypoints) { var startNode = allPathFindingWaypoints[startNodeIndex]; var targetNode = allPathFindingWaypoints[targetNodeIndex]; Heap openSet = new Heap(allPathFindingWaypoints.Length); HashSet closedSet = new HashSet(); openSet.Add(startNode); while (openSet.Count > 0) { PathFindingWaypoint currentNode = openSet.RemoveFirst(); closedSet.Add(currentNode); if (currentNode == targetNode) { return RetracePath(startNode, targetNode, allPathFindingWaypoints); } for (int i = 0; i < currentNode.Neighbours.Length; i++) { PathFindingWaypoint neighbour = allPathFindingWaypoints[currentNode.Neighbours[i]]; if (!neighbour.AllowedAgents.Contains(pedestraianTypes) || closedSet.Contains(neighbour)) { continue; } int newMovementCostToNeighbour = currentNode.GCost + GetDistance(currentNode, neighbour) + currentNode.MovementPenalty[i]; if (newMovementCostToNeighbour < neighbour.GCost || !openSet.Contains(neighbour)) { neighbour.GCost = newMovementCostToNeighbour; neighbour.HCost = GetDistance(neighbour, targetNode); neighbour.Parent = currentNode.ListIndex; if (!openSet.Contains(neighbour)) openSet.Add(neighbour); else { openSet.UpdateItem(neighbour); } } } } return null; } private List RetracePath(PathFindingWaypoint startNode, PathFindingWaypoint endNode, PathFindingWaypoint[] allPathFindingWaypoints) { List path = new List(); PathFindingWaypoint currentNode = endNode; while (currentNode != startNode) { path.Add(currentNode.ListIndex); currentNode = allPathFindingWaypoints[currentNode.Parent]; } path.Reverse(); return path; } /// /// Approximate distance between 2 waypoints. /// private int GetDistance(PathFindingWaypoint nodeA, PathFindingWaypoint nodeB) { float dstX = Mathf.Abs(nodeA.WorldPosition.x - nodeB.WorldPosition.x); float dstY = Mathf.Abs(nodeA.WorldPosition.z - nodeB.WorldPosition.z); if (dstX > dstY) return (int)(14 * dstY + 10 * (dstX - dstY)); return (int)(14 * dstX + 10 * (dstY - dstX)); } } }