以下是一个简单的Unity A*寻路例子:
1. 创建一个空的游戏对象,将其命名为“A*”,在其上添加一个空的脚本组件“AStarPathfinding”。
2. 在脚本中添加以下代码:
```
public class AStarPathfinding : MonoBehaviour {
public Transform startNode;
public Transform endNode;
public GameObject nodePrefab;
public LayerMask unwalkableMask;
public float nodeRadius;
public float gridSize;
public Vector2 gridWorldSize;
Node[,] grid;
void Start() {
CreateGrid();
FindPath(startNode.position, endNode.position);
}
void CreateGrid() {
int gridSizeX = Mathf.RoundToInt(gridWorldSize.x / gridSize);
int gridSizeY = Mathf.RoundToInt(gridWorldSize.y / gridSize);
grid = new Node[gridSizeX, gridSizeY];
Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x / 2 - Vector3.forward * gridWorldSize.y / 2;
for (int x = 0; x < gridSizeX; x++) {
for (int y = 0; y < gridSizeY; y++) {
Vector3 worldPoint = worldBottomLeft + Vector3.right * (x * gridSize + nodeRadius) + Vector3.forward * (y * gridSize + nodeRadius);
bool walkable = !(Physics.CheckSphere(worldPoint, nodeRadius, unwalkableMask));
grid[x, y] = new Node(walkable, worldPoint, x, y);
}
}
}
void FindPath(Vector3 startPos, Vector3 targetPos) {
Node startNode = NodeFromWorldPoint(startPos);
Node targetNode = NodeFromWorldPoint(targetPos);
List<Node> openSet = new List<Node>();
HashSet<Node> closedSet = new HashSet<Node>();
openSet.Add(startNode);
while (openSet.Count > 0) {
Node currentNode = openSet[0];
for (int i = 1; i < openSet.Count; i++) {
if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)) {
currentNode = openSet[i];
}
}
openSet.Remove(currentNode);
closedSet.Add(currentNode);
if (currentNode == targetNode) {
RetracePath(startNode, targetNode);
return;
}
foreach (Node neighbor in GetNeighbors(currentNode)) {
if (!neighbor.walkable || closedSet.Contains(neighbor)) {
continue;
}
int newMovementCostToNeighbor = currentNode.gCost + GetDistance(currentNode, neighbor);
if (newMovementCostToNeighbor < neighbor.gCost || !openSet.Contains(neighbor)) {
neighbor.gCost = newMovementCostToNeighbor;
neighbor.hCost = GetDistance(neighbor, targetNode);
neighbor.parent = currentNode;
if (!openSet.Contains(neighbor)) {
openSet.Add(neighbor);
}
}
}
}
}
List<Node> GetNeighbors(Node node) {
List<Node> neighbors = new List<Node>();
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0) {
continue;
}
int checkX = node.gridX + x;
int checkY = node.gridY + y;
if (checkX >= 0 && checkX < grid.GetLength(0) && checkY >= 0 && checkY < grid.GetLength(1)) {
neighbors.Add(grid[checkX, checkY]);
}
}
}
return neighbors;
}
Node NodeFromWorldPoint(Vector3 worldPosition) {
float percentX = (worldPosition.x + gridWorldSize.x / 2) / gridWorldSize.x;
float percentY = (worldPosition.z + gridWorldSize.y / 2) / gridWorldSize.y;
percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY);
int x = Mathf.RoundToInt((grid.GetLength(0) - 1) * percentX);
int y = Mathf.RoundToInt((grid.GetLength(1) - 1) * percentY);
return grid[x, y];
}
void RetracePath(Node startNode, Node endNode) {
List<Node> path = new List<Node>();
Node currentNode = endNode;
while (currentNode != startNode) {
path.Add(currentNode);
currentNode = currentNode.parent;
}
path.Reverse();
foreach (Node node in path) {
Instantiate(nodePrefab, node.worldPosition, Quaternion.identity);
}
}
int GetDistance(Node nodeA, Node nodeB) {
int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX);
int dstY = Mathf.Abs(nodeA.gridY - nodeB.gridY);
if (dstX > dstY) {
return 14 * dstY + 10 * (dstX - dstY);
} else {
return 14 * dstX + 10 * (dstY - dstX);
}
}
public class Node {
public bool walkable;
public Vector3 worldPosition;
public int gridX;
public int gridY;
public int gCost;
public int hCost;
public Node parent;
public Node(bool _walkable, Vector3 _worldPos, int _gridX, int _gridY) {
walkable = _walkable;
worldPosition = _worldPos;
gridX = _gridX;
gridY = _gridY;
}
public int fCost {
get {
return gCost + hCost;
}
}
}
}
```
3. 在场景中添加两个球体,将其中一个球体的Transform组件的位置设置为(-5, 0, 0),另一个球体的Transform组件的位置设置为(5, 0, 0),并将它们分别命名为“Start”和“End”。
4. 在场景中添加一个平面作为地图,将其缩放为(10, 1, 10),并将其位置设置为(0, -0.5, 0)。
5. 在场景中添加一个球体作为障碍物,将其缩放为(2, 2, 2),并将其位置设置为(0, 1, -2)。
6. 在“A*”游戏对象的脚本中,将“startNode”变量设置为“Start”游戏对象的Transform组件,将“endNode”变量设置为“End”游戏对象的Transform组件,将“nodePrefab”变量设置为一个球体预制体,将“unwalkableMask”变量设置为“Obstacle”层,将“nodeRadius”变量设置为0.5,将“gridSize”变量设置为1,将“gridWorldSize”变量设置为(10, 10)。
7. 运行游戏,可以看到在“Start”和“End”之间生成了一条路径,路径上的节点用球体表示。可以尝试改变地图和障碍物的位置和大小,并在脚本中调整相关变量,观察寻路结果的变化。