Unity敌人AI逻辑问题:实现步行触发后才可切换奔跑状态
问题分析与修复方案
原代码核心问题
- 错误手动调用Unity回调:
OnBecameVisible()是Unity内置的状态回调,仅在对象从不可见变为可见时触发一次,无需在Update()中循环调用,这会导致逻辑触发时机完全混乱。 - 状态逻辑缺失:未实现“敌人距离玩家2米以内切换奔跑”的需求,且
isWalking的状态切换逻辑模糊,无法完成行走/奔跑的正确切换。 - 不可见状态处理错误:
OnBecomeInvisible()直接禁用脚本会导致敌人重新可见时无法恢复逻辑,应改为停止移动并重置状态。 - 输入检测位置错误:空格键输入需在
Update()中持续监听,而非仅在可见状态变化时检测,否则无法捕捉可见状态下的多次按键操作。
修正后的完整代码
using UnityEngine; using UnityEngine.AI; public class EnemyAI : MonoBehaviour { public NavMeshAgent enemy; public Transform player; public float speedWalk = 6f; public float speedRun = 12f; // 原60f速度过高,调整为合理值 public float chaseDelay = 2f; public float runTriggerDistance = 2f; // 触发奔跑的距离阈值 private bool isWalking; private bool isRunning; private Renderer m_Renderer; private Coroutine chaseCoroutine; private void Start() { m_Renderer = GetComponent<Renderer>(); isWalking = false; isRunning = false; enemy.speed = speedWalk; enemy.isStopped = true; // 初始状态停止移动 } private void OnBecameVisible() { // 敌人可见时恢复移动权限 enemy.isStopped = false; } private void OnBecameInvisible() { // 敌人不可见时停止移动并重置所有状态 enemy.isStopped = true; isWalking = false; isRunning = false; if (chaseCoroutine != null) { StopCoroutine(chaseCoroutine); chaseCoroutine = null; } } private IEnumerator StartChaseWithDelay() { yield return new WaitForSeconds(chaseDelay); // 延迟结束后进入行走状态 isWalking = true; isRunning = false; enemy.speed = speedWalk; enemy.SetDestination(player.position); } private void Update() { // 仅当敌人可见时处理逻辑 if (!m_Renderer.isVisible) return; float distanceToPlayer = Vector3.Distance(transform.position, player.position); // 处理空格键单次按下事件 if (Input.GetKeyDown(KeyCode.Space)) { if (!isWalking && !isRunning) { // 未移动状态下启动延迟追逐 if (chaseCoroutine == null) { chaseCoroutine = StartCoroutine(StartChaseWithDelay()); } } else if (isWalking) { // 行走状态下切换为奔跑 isWalking = false; isRunning = true; enemy.speed = speedRun; enemy.SetDestination(player.position); } } // 距离玩家2米以内自动切换奔跑 if ((isWalking || isRunning) && distanceToPlayer <= runTriggerDistance) { isWalking = false; isRunning = true; enemy.speed = speedRun; enemy.SetDestination(player.position); } // 持续更新目标位置,追踪移动的玩家 if (isWalking || isRunning) { enemy.SetDestination(player.position); } } }
关键修改说明
- 回调逻辑规范:仅保留
OnBecameVisible()和OnBecameInvisible()的状态切换作用,不再手动调用。 - 状态清晰划分:新增
isRunning变量,明确区分停止、行走、奔跑三种状态。 - 输入逻辑优化:使用
Input.GetKeyDown()检测单次按键,避免持续按住触发重复操作,且将输入检测放在Update()中持续监听。 - 距离触发实现:添加距离阈值变量,实时计算敌人与玩家的距离,满足“2米以内切换奔跑”的需求。
- 不可见状态修复:改为停止移动并重置状态,而非禁用脚本,确保敌人重新可见时能正常恢复逻辑。
- 目标持续追踪:在移动状态下持续更新目标位置,保证敌人能跟随移动的玩家。
内容的提问来源于stack exchange,提问作者user21241519
相关产品推荐
相关产品推荐

