Unity A* Pathfinding Project Pro 2D项目如何选取网格随机位置供敌人AI移动?
2D项目基于A* Pathfinding Project Pro的寻路网格随机点选取方案
直接在地图矩形范围内生成随机坐标是实现随机巡逻最常见的错误写法,这种方式大概率会把目标点选到寻路网格标记的障碍、边界外区域,导致敌人寻路卡墙、站在原地不动。正确的实现逻辑是从已经烘焙完成的可通行寻路节点范围内做采样,校验点位合法性后再作为移动目标。
实现核心步骤
- 启动阶段一次性缓存场景内的
GridGraph(2D项目默认使用的平面网格寻路图),避免每次生成随机点都遍历查找全场景寻路图,造成不必要的性能消耗 - 基于缓存的网格图范围生成随机坐标,通过寻路系统的最近节点查询接口拿到对应位置的寻路节点
- 逐层校验节点合法性:首先确认节点处于可通行状态,其次确认点位和敌人当前位置的距离符合巡逻的范围要求,校验通过后取节点对应的世界坐标作为巡逻目标
- 将通过校验的目标点传给
AIPath组件的destination属性,即可驱动敌人沿优化后的寻路路径移动
2D场景适配注意事项
- 无需使用3D物理射线做点位校验,网格烘焙时已经集成了2D碰撞、Tilemap障碍的通行标记,直接读取节点自带的
Walkable属性即可判断点位是否可走 - 随机采样的范围要在网格原始Bounds基础上向内收缩至少0.5个节点直径的宽度,避免随机点刚好落在网格边缘的障碍上,导致敌人卡在边界位置
- 如果需要限制敌人的巡逻活动范围,可以在采样时额外加一层矩形/圆形范围判断,只保留指定活动区域内的合法随机点
可直接复用的实现代码
using UnityEngine; using Pathfinding; public class EnemyRandomPatrolPointGetter : MonoBehaviour { [Tooltip("随机点不能离当前位置太近,避免敌人小幅度来回晃")] public float minDistanceFromCurrentPos = 3f; // 每次选随机点的最大重试次数,避免极端场景下死循环 public int maxSampleRetryCount = 10; // 缓存的2D寻路网格 private GridGraph _cachedGridGraph; // 缓存的AIPath移动组件 private AIPath _aiPath; private void Awake() { // 启动时一次性拿到场景内的2D网格寻路图 _cachedGridGraph = (GridGraph)AstarPath.active.data.GetGraph(graph => graph is GridGraph); _aiPath = GetComponent<AIPath>(); } /// <summary> /// 从寻路网格上生成符合要求的合法随机巡逻点 /// </summary> public Vector2 GetValidRandomPatrolPoint() { // 拿到网格的范围,向内收缩避免卡边界 Bounds graphSampleBounds = _cachedGridGraph.bounds; graphSampleBounds.Expand(-_cachedGridGraph.nodeSize * 0.5f); for (int retry = 0; retry < maxSampleRetryCount; retry++) { // 在收缩后的范围内生成2D平面随机坐标 float randomX = Random.Range(graphSampleBounds.min.x, graphSampleBounds.max.x); float randomY = Random.Range(graphSampleBounds.min.y, graphSampleBounds.max.y); Vector3 randomWorldPos = new Vector3(randomX, randomY, 0); // 查询随机坐标对应的最近可通行节点 GraphNode sampledNode = AstarPath.active.GetNearest( point: randomWorldPos, constraint: NNConstraint.Walkable).node; // 节点不可通行直接跳过 if (!sampledNode.Walkable) continue; // 换算成2D平面坐标 Vector2 sampledNodeWorldPos = (Vector2)sampledNode.position; // 距离当前位置太近的点位跳过 if (Vector2.Distance(sampledNodeWorldPos, (Vector2)transform.position) < minDistanceFromCurrentPos) continue; // 所有校验通过,返回合法点位 return sampledNodeWorldPos; } // 多次重试都没找到合法点,返回当前位置避免报错 return (Vector2)transform.position; } // 巡逻逻辑:到达当前点位后自动生成下一个随机目标 private void Update() { // 当敌人没有锁定目标时,持续保持随机巡逻状态 if (_aiPath.reachedEndOfPath) { Vector2 nextTarget = GetValidRandomPatrolPoint(); _aiPath.destination = nextTarget; } } }
脚本直接挂载在敌人对象上,搭配同对象的AIPath组件、场景内烘焙完成的GridGraph即可生效,敌人到达当前巡逻点后会自动生成下一个符合距离要求的随机目标,在未锁定玩家的阶段保持巡逻状态。
高频踩坑提醒
- 不要绕过寻路系统直接把Unity随机生成的世界坐标丢给
AIPath当目标,一旦点位处于不可达状态,敌人会卡在原地反复重新计算路径,甚至出现穿墙寻路的逻辑异常 - 调整GridGraph的配置时,要确保网格平面和2D角色的移动平面完全重合,不要出现网格图和角色存在Z轴偏移的情况,否则采样的点位会和角色不在同一平面,出现寻路路径偏移的问题
- 如果项目存在动态生成的寻路障碍,采样时一定要使用
NNConstraint.Walkable的约束参数,确保拿到的节点是实时状态下的可通行点,不会跑到被临时障碍挡住的区域
内容的提问来源于stack exchange,提问作者Tundraswan
相关产品推荐
相关产品推荐

