using System; 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 GridXZ grid; private List openList; private List closedList; public Pathfinding(int widht, int height) { instance = this; grid = new GridXZ(widht, height, 10f, Vector3.zero, (GridXZ g, int x, int y) => new PathNode(g, x, y)); } public GridXZ GetGrid() { return grid; } public List FindPath(Vector3 startWorldPosition, Vector3 enWorldPosition) { grid.GetXZ(startWorldPosition, out int startX, out int startZ); grid.GetXZ(enWorldPosition, out int endX, out int endZ); List path = FindPath(startX, startZ, endX, endZ); if (path == null) { return null; } else { List vectorPath = new List(); foreach (PathNode pathNode in path) { vectorPath.Add(new Vector3(pathNode.x, pathNode.z) * grid.GetCellSize() + Vector3.one * grid.GetCellSize() * .5f); } return vectorPath; } } public List FindPath(int startX, int startZ, int endX, int endZ) { PathNode startNode = grid.GetGridObject(startX, startZ); PathNode endNode = grid.GetGridObject(endX, endZ); if (endNode == null || startNode == null) { return null; } openList = new List { startNode }; closedList = new List(); for (int x = 0; x < grid.GetWidth(); x++) { for (int z = 0; z < grid.GetHeight(); z++) { PathNode pathNode = grid.GetGridObject(x, z); pathNode.gCost = int.MaxValue; pathNode.CalculateFCost(); pathNode.cameFromNode = null; } } startNode.gCost = 0; Debug.Log("endX: " + endX + " endY: " + endZ); startNode.hCost = CalculateDistanceCost(startNode, endNode); startNode.CalculateFCost(); while (openList.Count > 0) { PathNode currentNode = GetLowestFCostNode(openList); if (currentNode == endNode) { // Reached final node // PathfindingDebugStepVisual.Instance.TakeSnapshot(grid, currentNode, openList, closedList); // PathfindingDebugStepVisual.Instance.TakeSnapshotFinalPath(grid, CalculatePath(endNode)); 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); } } // PathfindingDebugStepVisual.Instance.TakeSnapshot(grid, currentNode, openList, closedList); } } // Out of nodes on the open list return null; } public PathNode GetNode(int x, int z) { return grid.GetGridObject(x, z); } private List GetNeighbourList(PathNode currentNode) { List neighbourList = new List(); if (currentNode.x - 1 >= 0) { // Left neighbourList.Add(GetNode(currentNode.x - 1, currentNode.z)); // Left Down if (currentNode.z - 1 >= 0) neighbourList.Add(GetNode(currentNode.x - 1, currentNode.z - 1)); // Left Up if (currentNode.z + 1 < grid.GetHeight()) neighbourList.Add(GetNode(currentNode.x - 1, currentNode.z + 1)); } if (currentNode.x + 1 < grid.GetWidth()) { // Right neighbourList.Add(GetNode(currentNode.x + 1, currentNode.z)); // Right Down if (currentNode.z - 1 >= 0) neighbourList.Add(GetNode(currentNode.x + 1, currentNode.z - 1)); // Right Up if (currentNode.z + 1 < grid.GetHeight()) neighbourList.Add(GetNode(currentNode.x + 1, currentNode.z + 1)); } // Down if (currentNode.z - 1 >= 0) neighbourList.Add(GetNode(currentNode.x, currentNode.z - 1)); // Up if (currentNode.z + 1 < grid.GetHeight()) neighbourList.Add(GetNode(currentNode.x, currentNode.z + 1)); return neighbourList; } 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.z - b.z); 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; } }