我们已在A星寻路算法一:算法中介绍了A星算法的原理,这节主要实现节点网格。
1.新建一个Plane与Cube,并标记它们的位置为(0, 0, 0),Plane的缩放值为(3,3,3)。
2.新建两个材质,一个Ground用于更改地面的颜色,一个Obstacle用于更改障碍物的颜色。
3.给Cube添加Layer为Unwalkable,并多复制一些Cube,增加更多障碍物。
4.新增A*节点,并标记坐标为 0,0,0
并把该节点放至在最顶层便于查找。
5.新建两个新的脚本文件,并命名Grid与Node,其中把Grid附加到A*节点中作为GameObject组件。
using UnityEngine;
using System.Collections;
// 网格管理器类,用于生成和管理游戏中的网格系统
public class Grid : MonoBehaviour {
// 玩家所在节点的引用(在Unity编辑器中拖拽赋值)
public Transform PlayerNode;
// 不可行走的图层掩码(用于检测障碍物)
public LayerMask unwalkableMask;
// 网格的世界尺寸(x 和 y 方向)
public Vector2 gridWorldSize;
// 每个节点的半径(用于计算节点大小)
public float nodeRadius;
// 网格节点数组(二维数组存储所有节点)
Node[,] grid;
// 节点的直径(由半径计算得出)
float nodeDiameter;
// 网格在 x 和 y 方向的节点数量
int gridSizeX, gridSizeY;
// Unity 的 Start 方法(初始化网格)
void Start() {
// 计算节点直径(半径 * 2)
nodeDiameter = nodeRadius * 2;
// 根据网格世界尺寸和节点直径计算网格在 x 和 y 方向的节点数量
gridSizeX = Mathf.RoundToInt(gridWorldSize.x / nodeDiameter);
gridSizeY = Mathf.RoundToInt(gridWorldSize.y / nodeDiameter);
// 创建网格
CreateGrid();
}
// 创建网格的方法
void CreateGrid() {
// 初始化网格数组
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 * nodeDiameter + nodeRadius)
+ Vector3.forward * (y * nodeDiameter + nodeRadius);
// 检测该位置是否有障碍物(不可行走)
bool walkable = !(Physics.CheckSphere(worldPoint, nodeRadius, unwalkableMask));
// 创建节点并存储到网格数组中
grid[x, y] = new Node(walkable, worldPoint);
}
}
}
// 将世界坐标转换为对应的网格节点
public Node NodeFromWorldPoint(Vector3 worldPosition) {
// 计算世界坐标在网格中的百分比位置(x 方向)
float percentX = (worldPosition.x + gridWorldSize.x / 2) / gridWorldSize.x;
// 计算世界坐标在网格中的百分比位置(y 方向)
float percentY = (worldPosition.z + gridWorldSize.y / 2) / gridWorldSize.y;
// 限制百分比在 0~1 范围内(防止越界)
percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY);
// 根据百分比计算网格索引(x 和 y 方向)
int x = Mathf.RoundToInt((gridSizeX - 1) * percentX);
int y = Mathf.RoundToInt((gridSizeY - 1) * percentY);
// 返回对应的网格节点
return grid[x, y];
}
// 在 Scene 视图中绘制网格和节点(仅编辑器中可见)
void OnDrawGizmos() {
// 绘制网格的外框(线框立方体)
Gizmos.DrawWireCube(transform.position, new Vector3(gridWorldSize.x, 1, gridWorldSize.y));
// 如果网格已生成
if (grid != null) {
// 获取玩家所在节点
Node playerNode = NodeFromWorldPoint(PlayerNode.position);
// 遍历所有节点并绘制
foreach (Node n in grid) {
// 根据节点是否可行走设置颜色(可行走为白色,不可行走为红色)
Gizmos.color = (n.walkable) ? Color.white : Color.red;
// 如果是玩家所在节点,设置为绿色
if (playerNode == n) {
Gizmos.color = Color.green;
}
// 绘制节点的立方体(略小于节点直径以避免重叠)
Gizmos.DrawCube(n.worldPosition, Vector3.one * (nodeDiameter - 0.1f));
}
}
}
}
6.最后的效果如下:
其中红色区域是遮挡区域,绿色是当前角色的位置,灰色区域是网格: