Unity敌人自动寻路
基于NavMesh(导航网格)系统的自动寻路图为jump烘焙准备工作项目里有的各种地形例如开始——”我的资产“插件AI Navigation地面烘焙Ⅱ在“层级”中创建AI——NavMesh Surfaceor找个物体挂载NavMesh Surface组件设置好参数后烘焙↓************************************************************************************************************↓NavMesh Surface组件详情*Agent Type(代理类型)定义当前烘焙出的网格是以什么角色数值为基础烘焙的。角色身上的NavMesh Agent组件必须与此处的设置相匹配才能在该网格上正确寻路。可自定义角色类型与数值图中角色是Humanoid,该角色高2米,半径0.5可在45°坡上行走能跨过0.75的落差*Default Area(默认区域)设定烘焙出的网格默认具备什么属性。最常用的是Walkable可行走此外还有Not Walkable不可行走如悬崖或Jump跳跃区域等。封面是jump烘焙*Generate Links(生成链接)作用自动生成连接不同 NavMesh 区域的可行走路径例如跨越缝隙的跳板。*Use Geometry(使用什么几何体)——Render Meshes使用美术看到的精美模型如复杂的房屋。缺点是烘焙慢且 AI 可能会贴着装饰性的栏杆走。——Physics Colliders使用碰撞体。优点是 AI 会贴着物理边界边缘行走且烘焙速度快通常推荐使用此项。Object Collection (对象收集)-Collect Objects (收集对象)——All Game Objects所有游戏对象——体积Volume可设置烘焙大小用于局部烘焙——Current Object Hierarchy当前对象层级这是一种预制体Prefab内部烘焙模式。它只会抓取挂载了该NavMesh Surface组件本身的那个物体及其所有的子物体来参与烘焙。你制作了一个包含楼梯和走廊的“塔楼”预制体。当你把这个塔楼拖入主场景时你希望塔楼内部的敌人只在塔楼里寻路。此时将这个选项设为Current Object Hierarchy塔楼的 NavMesh 就只基于它自己的模型生成完美独立。——NavMeshModifier Component Only仅导航网格修改器组件在此模式下NavMesh Surface本身不收集任何实际的场景几何体。它的作用是专门用来抓取场景中所有带有NavMeshModifier组件的物体将这些组件上设置的属性如将某区域设为“不可行走”或提高“通行代价”应用到底层的导航网格数据中。你有一大片草地你想让 AI 尽量不走某条特定的小路。你不需要修改草地的模型而是放几个空物体给它们加上NavMeshModifier组件并设为Not Walkable。使用此收集模式烘焙时就会把这些“不可行走”的规则提取出来应用到最终的网格上。-Include Layers包含层 (包含层)选择层级Advanced(高级)-Override Voxel Size (覆盖体素大小)手动控制体素的大小。大精度高小性能高-Override Tile Size (覆盖瓦片大小)为了提高内存效率和并行计算NavMesh 会被切分成许多个小方块瓦片来处理。大精度高小性能高比如说非常多动态障碍物网格重算频繁导致卡顿此时调小可以让重算速度变快减少游戏卡顿-Minimum Region Area (最小区域面积)-Build Height Mesh (构建高度网格)目前通常不推荐使用未完全实现NavMesh Data(导航网格数据)显示当前引用的网格资源文件。↑************************************************************************************************************↑烘焙这是烘焙后的网格文件↓************************************************************************************************************↓可以通过不同的参数组合实现局部烘焙指定物品烘焙空气墙等不同的烘焙例如通过图层实现只烘焙地面变成↑************************************************************************************************************↑角色挂载Ⅰ一般来说先写好Nav Mesh Agent角色类型再烘焙地面网格给需要自动寻路的物体挂载Nav Mesh Agent组件一般是Enemy敌人这是新创建的Player与敌人分别为他们设置了标签Player与Enemy图层Player与Enemy方便后面寻路代码的实现自行修改想要的参数只有这个必须与NavMesh Surface组件中的代理类型一致。代码using UnityEngine; public class Xunlu : MonoBehaviour { [Header(基础设置)] public float detectionRange 22f; // 检测范围 public float moveSpeed 3f; // 移动速度 public float stoppingDistance 1.5f; // 停止距离 public LayerMask obstacleLayers; // 障碍物层级在Inspector中设置 [Header(调试)] public bool showDebugGizmos true; // 显示调试图形 public Color gizmoColor Color.yellow; // Gizmo颜色 private Transform player; // 玩家Transform private bool canSeePlayer false; // 是否能看见玩家 private Vector3 lastKnownPosition; // 最后已知位置 void Start() { // 查找玩家 GameObject playerObj GameObject.FindWithTag(Player); if (playerObj ! null) { player playerObj.transform; } else { Debug.LogWarning(未找到带有Player标签的游戏对象); } // 如果没有设置障碍物层级默认包含Everything if (obstacleLayers.value 0) { obstacleLayers ~0; // 包含所有层级 } } void Update() { if (player null) return; // 检查玩家是否在检测范围内 float distanceToPlayer Vector3.Distance(transform.position, player.position); if (distanceToPlayer detectionRange) { // 射线检测玩家是否被遮挡 canSeePlayer CheckPlayerVisibility(); if (canSeePlayer) { lastKnownPosition player.position; ChasePlayer(); } else { // 看不见玩家时的行为可选前往最后已知位置 // MoveToLastKnownPosition(); } } else { canSeePlayer false; } } /// summary /// 检查玩家是否可见射线检测 /// /summary bool CheckPlayerVisibility() { if (player null) return false; Vector3 directionToPlayer player.position - transform.position; Ray ray new Ray(transform.position, directionToPlayer.normalized); // 绘制调试射线 Debug.DrawRay(transform.position, directionToPlayer.normalized * detectionRange, canSeePlayer ? Color.green : Color.red); // 射线检测忽略触发器 RaycastHit hit; if (Physics.Raycast(ray, out hit, detectionRange, obstacleLayers, QueryTriggerInteraction.Ignore)) { // 检查击中的是否是玩家 if (hit.collider.CompareTag(Player)) { return true; } } return false; } /// summary /// 追击玩家 /// /summary void ChasePlayer() { if (player null) return; float distanceToPlayer Vector3.Distance(transform.position, player.position); // 如果距离大于停止距离则移动 if (distanceToPlayer stoppingDistance) { // 计算移动方向 Vector3 direction (player.position - transform.position).normalized; // 移动 transform.Translate(direction * moveSpeed * Time.deltaTime, Space.World); // 面向玩家可选 transform.LookAt(new Vector3(player.position.x, transform.position.y, player.position.z)); } } /// summary /// 移动到最后已知位置可选功能 /// /summary void MoveToLastKnownPosition() { float distanceToLastPos Vector3.Distance(transform.position, lastKnownPosition); if (distanceToLastPos stoppingDistance) { Vector3 direction (lastKnownPosition - transform.position).normalized; transform.Translate(direction * moveSpeed * Time.deltaTime, Space.World); transform.LookAt(new Vector3(lastKnownPosition.x, transform.position.y, lastKnownPosition.z)); } } /// summary /// 绘制调试图形 /// /summary void OnDrawGizmos() { if (!showDebugGizmos) return; // 绘制检测范围 Gizmos.color gizmoColor; Gizmos.DrawWireSphere(transform.position, detectionRange); // 绘制停止距离 Gizmos.color Color.red; Gizmos.DrawWireSphere(transform.position, stoppingDistance); // 如果正在追击绘制到玩家的连线 if (Application.isPlaying canSeePlayer player ! null) { Gizmos.color Color.green; Gizmos.DrawLine(transform.position, player.position); } } }将此脚本组件挂载到敌人身上范围内追击玩家Player标签敌人“看见”玩家才追击射线层级这里只用到了Player标签与障碍物的“层级”*注意玩家与敌人都要处于烘焙好的网格上才行额外台阶/斜坡图为正常烘培当我们不需要台阶或斜坡时方法一(全部取消烘焙)window(窗口)——Ai——Navigation(导航)改可迈过高度斜坡°再次烘焙完成方法二(指定物体取消烘焙)同天花板天花板单独物体烘焙在天花板物体上添加组件Nav Mesh modifier方法一Remove Object模式修改模式为‘Remove Objecdt’(移除)此时此物体将不会被烘焙↓************************************************************************************************************↓Affected Agents(受影响的代理)作用筛选受影响的角色类型自定义的角色类型Apply To Children(是否应用到子物体)↑************************************************************************************************************↑再次烘焙完成方法二 Add or Modify Object模式更精准↓************************************************************************************************************↓Affected Agents(受影响的代理)作用筛选受影响的角色类型自定义的角色类型Apply To Children(是否应用到子物体)Override Area是否为覆盖区域作用通常 Unity 会自动判断地面是否可行走比如悬空的平台默认是不可行走的。勾选这两项的意思是无论这个物体的物理形状如何哪怕它是悬空的都强制要求 Unity 把它算作“地面”允许 AI 在上面寻路。I√ ——Area Type作为什么烘焙类型的覆盖区域Override Generate Links是否覆盖生成链接I√ ——Generate Links是否自动生成链接作用如果生成了链接AI 可能会尝试从平台边缘跳下或跳向对面√自动生成链接的效果口不自动生成链接的效果↑************************************************************************************************************↑作为“不走路”类型的区域烘焙烘焙完成门动态障碍物烘焙添加组件Nav Mesh Obstacle↓************************************************************************************************************↓*切割*不勾选默认障碍物只是“飘浮”在导航网格上方的一层像一块地毯。AI 只能在网格未被覆盖的地方走。缺点如果 AI 已经走在网格被覆盖的区域它可能不会立刻反应过来要躲避。*勾选障碍物会像刀一样直接在底层导航网格上切出一个洞。优点AI 的寻路算法会实时避开这个洞即使它正在走向这里也会立即寻找新路径。性能消耗较大建议对移动的大物体使用。移动阈值挪多远才算动动了系统才会认为它需要更新路径静止时间停多久才算彻底停稳停下时才在地图上把“路障”真正立起来。仅在静止时切割勾选适用于被物理撞击的物体如箱子、木桶取消勾选适用于缓慢移动的大型载具如坦克、巡逻车↑************************************************************************************************************↑如图设置自己微调完成门的效果END