Unity AI巡逻与NavMesh追击切换else分支不生效问题排查
问题背景
- 已实现基础点位巡逻逻辑:通过Transform数组存储所有巡逻点位,AI按顺序逐点移动,遍历完成后回到首个点位循环巡逻。
- 预期实现射线检测触发的状态切换:射线检测到敌人时,停止巡逻逻辑、启用NavMeshAgent进入追击状态;未检测到敌人时,关闭NavMeshAgent、启用巡逻逻辑回到巡逻状态。实际运行时未检测到敌人的分支始终无法正常触发。
现有实现代码
巡逻脚本代码
public Transform[] Points; public int current; public NavMeshAgent agent; public milkscript milk; public float speed; // Start is called before the first frame update void Start() { current = 0; agent.enabled = false; } // Update is called once per frame void Update() { if(milk.chasing == false) { agent.enabled = false; Patol(); } if(milk.chasing == true) { agent.enabled = true; } } public void Patol() { if(transform.position != Points[current].position) { transform.position = Vector3.MoveTowards(transform.position,Points[current].position, speed * Time.deltaTime); } else { // 此处运算优先级错误,会导致索引无法正常循环 current = current+1 % Points.Length; } // 硬编码重置索引,巡逻点数量不为8时会数组越界 if(current == 8) { current = 0; } }
射线检测/追击脚本代码
NavMeshAgent Agent; public Patrol patrol; public bool chasing; public Transform enemy; public Transform[] Points; [SerializeField] LayerMask layermask; public Transform Point; public float raydistance = 10f; public float enemyview = 5f; // Start is called before the first frame update void Start() { Agent = GetComponent<NavMeshAgent>(); chasing = false; } // Update is called once per frame void Update() { if(Physics.Raycast(Point.position, Point.forward, out var Hit, raydistance, layermask)) { Debug.DrawLine(Point.position, Hit.transform.position, Color.red); Debug.Log(Hit.transform.name); if(Hit.transform.name == "Chocltaemilk") { chasing = true; // 此处逻辑和后续状态判断冲突 patrol.enabled = true; Agent.SetDestination(Hit.transform.position); } // 缺少关键逻辑:射线检测到其他非目标物体时,没有重置chasing状态 } else { Agent.enabled = false; Debug.DrawRay(Point.position, Point.forward, Color.green); patrol.Patol(); } if(chasing == false) { Agent.enabled = false; patrol.enabled = true; } if(chasing == true) { Agent.enabled = true; patrol.enabled = false; } } void returen() { chasing = false; patrol.enabled = false; } public void ChasePlayer(Transform target) { Agent.SetDestination(target.position); transform.LookAt(target.position); }
核心问题点与修复方案
追击状态无法正常退出是核心bug
射线检测逻辑只在「检测到的物体刚好是目标敌人」时把chasing设为true,但只要射线扫到了分层里的其他物体(地面、墙、场景道具),Physics.Raycast就会返回true进入if分支,此时因为物体名不匹配,不会修改chasing的值,导致chasing一旦设为true就再也没机会被改回false,else分支自然永远不会正常执行。
修复方式:在射线检测到非目标物体时,增加chasing = false的状态重置;也可以加个丢失目标计时,连续N帧没检测到敌人再切回巡逻,避免状态反复横跳。巡逻点索引计算逻辑错误
代码里current = current+1 % Points.Length;存在运算符优先级问题,取模%优先级高于加法+,实际执行逻辑是current = current + (1 % Points.Length),索引只会一直累加不会循环,后面硬编码current == 8重置的写法通用性极差,只要巡逻点数量不是8就会触发数组越界。
修复方式:改成current = (current + 1) % Points.Length;,直接删掉后面判断current等于8重置的冗余代码。双脚本重复控制状态,逻辑互相覆盖
巡逻脚本和追击脚本都在Update里反复开关NavMeshAgent、反复控制巡逻脚本的启用状态,两边逻辑不同步很容易出现状态被覆盖的问题。比如追击脚本else分支刚调用完巡逻方法,后面紧接着的if(chasing == true)又会把巡逻脚本关掉,等于前面的逻辑白执行。
修复方式:把状态切换的逻辑统一收敛到一个脚本里,不要两个脚本同时修改组件启用状态和AI行为。追击逻辑不完整
目前只有第一次检测到敌人时会调用一次SetDestination,敌人移动时NavMeshAgent不会实时更新追击目标点,会出现追着空气跑的问题。修复方式是在追击状态下每帧调用SetDestination更新目标位置。
内容的提问来源于stack exchange,提问作者Edwingocode

