using System.Collections; using System.Collections.Generic; using UnityEngine; public class Pathfinding { private const int MOVE_STRAIGHT_COST = 10; private const int MOVE_DIAGONAL_COST = 14; public static Pathfinding Instance { get; private set; } private Grid grid; private List openList; private List closedList; public Pathfinding(int width, int height) { Instance = this; grid = new Grid(width, height, 1f, Vector3.zero, (Grid g, int x, int y) => new PathNode(g, x, y)); } public Grid GetGrid() { return grid; } public List FindPath(Vector3 startWorldPosition, Vector3 endWorldPosition) { grid.GetXY(startWorldPosition, out int startX, out int startY); grid.GetXY(endWorldPosition, out int endX, out int endY); List path = FindPath(startX, startY, endX, endY); if (path == null) { return null; } else { List vectorPath = new List(); foreach (PathNode pathNode in path) { vectorPath.Add(new Vector3(pathNode.x, pathNode.y) * grid.GetCellSize() + Vector3.one * grid.GetCellSize() * .5f); } return vectorPath; } } public List FindPath(int startX, int startY, int endX, int endY) { PathNode startNode = grid.GetGridObject(startX, startY); PathNode endNode = grid.GetGridObject(endX, endY); if (startNode == null || endNode == null) { // Invalid Path return null; } openList = new List { startNode }; closedList = new List(); for (int x = 0; x < grid.GetWidth(); x++) { for (int y = 0; y < grid.GetHeight(); y++) { PathNode pathNode = grid.GetGridObject(x, y); pathNode.gCost = 99999999; pathNode.CalculateFCost(); pathNode.cameFromNode = null; } } startNode.gCost = 0; startNode.hCost = CalculateDistanceCost(startNode, endNode); startNode.CalculateFCost(); while (openList.Count > 0) { PathNode currentNode = GetLowestFCostNode(openList); if (currentNode == endNode) { // Reached final node return CalculatePath(endNode); } openList.Remove(currentNode); closedList.Add(currentNode); foreach (PathNode neighbourNode in GetNeighbourList(currentNode)) { if (closedList.Contains(neighbourNode)) continue; if (!neighbourNode.isWalkable) { closedList.Add(neighbourNode); continue; } int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNode, neighbourNode); if (tentativeGCost < neighbourNode.gCost) { neighbourNode.cameFromNode = currentNode; neighbourNode.gCost = tentativeGCost; neighbourNode.hCost = CalculateDistanceCost(neighbourNode, endNode); neighbourNode.CalculateFCost(); if (!openList.Contains(neighbourNode)) { openList.Add(neighbourNode); } } } } // Out of nodes on the openList return null; } private List GetNeighbourList(PathNode currentNode) { List neighbourList = new List(); if (currentNode.x - 1 >= 0) { // Left neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y)); // Left Down if (currentNode.y - 1 >= 0) neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y - 1)); // Left Up if (currentNode.y + 1 < grid.GetHeight()) neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y + 1)); } if (currentNode.x + 1 < grid.GetWidth()) { // Right neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y)); // Right Down if (currentNode.y - 1 >= 0) neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y - 1)); // Right Up if (currentNode.y + 1 < grid.GetHeight()) neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y + 1)); } // Down if (currentNode.y - 1 >= 0) neighbourList.Add(GetNode(currentNode.x, currentNode.y - 1)); // Up if (currentNode.y + 1 < grid.GetHeight()) neighbourList.Add(GetNode(currentNode.x, currentNode.y + 1)); return neighbourList; } public PathNode GetNode(int x, int y) { return grid.GetGridObject(x, y); } private List CalculatePath(PathNode endNode) { List path = new List(); path.Add(endNode); PathNode currentNode = endNode; while (currentNode.cameFromNode != null) { path.Add(currentNode.cameFromNode); currentNode = currentNode.cameFromNode; } path.Reverse(); return path; } private int CalculateDistanceCost(PathNode a, PathNode b) { int xDistance = Mathf.Abs(a.x - b.x); int yDistance = Mathf.Abs(a.y - b.y); int remaining = Mathf.Abs(xDistance - yDistance); return MOVE_DIAGONAL_COST * Mathf.Min(xDistance, yDistance) + MOVE_STRAIGHT_COST * remaining; } private PathNode GetLowestFCostNode(List pathNodeList) { PathNode lowestFCostNode = pathNodeList[0]; for (int i = 1; i < pathNodeList.Count; i++) { if (pathNodeList[i].fCost < lowestFCostNode.fCost) { lowestFCostNode = pathNodeList[i]; } } return lowestFCostNode; } }