Unity3D第三人称游戏锁定目标功能随机失效问题求助
第三人称游戏锁定目标功能问题排查与优化
问题说明
开发第三人称游戏锁定目标功能时,当前脚本存在随机无法锁定的问题,移动角色调整角度偶尔能恢复锁定。需要排查问题根源,同时添加目标可见性检测,确保目标大多时候处于可见状态。
原代码
public class PlayerLockOn : MonoBehaviour { public bool lockOn = false; float maxLockDist = 65; List<GameObject> availableTars = new List<GameObject>(); float shortestDist = Mathf.Infinity; public GameObject closestLockOnTarget; public GameObject currentTarget; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { //Get Input if (lockOn) { GetTargets(); ManageTargets(); if (currentTarget == null) { CameraMovement.instance.camRecenter(); lockOn = false; } else { PlayerStateManager.instance.camState = 1; CameraMovement.instance.lockOnTarget = currentTarget; } } else { availableTars.Clear(); currentTarget = null; CameraMovement.instance.lockOnTarget = null; closestLockOnTarget = null; PlayerStateManager.instance.camState = 0; } } void GetTargets() { Collider[] colliders = Physics.OverlapSphere(transform.position, 25); for (int b = 0; b < colliders.Length; b++) { var tag = colliders[b].tag; if (tag == "Enemy") { Transform tarTransform = colliders[b].transform; Vector3 TargetDir = transform.position - tarTransform.position; float distance = Vector3.Distance(tarTransform.position, transform.position); if (PlayerStateManager.instance.Player.transform.root != tarTransform.root && distance <= maxLockDist) { availableTars.Add(colliders[b].gameObject); } } } } void ManageTargets() { for (int i = 0; i < availableTars.Count; i++) { float dist = Vector3.Distance(transform.position, availableTars[i].transform.position); if (dist <= shortestDist) { shortestDist = dist; closestLockOnTarget = availableTars[i]; currentTarget = closestLockOnTarget; } } } public void lockOnUpdate() { lockOn = !lockOn; } }
问题根源排查
shortestDist未重置:shortestDist初始设为无穷大,但每次调用ManageTargets时没有重新赋值为无穷大。第一次锁定后,后续计算最短距离会沿用之前的最小值,导致新的目标即使在范围内,只要距离比之前的shortestDist大,就不会被选中,出现无法锁定的情况。- 检测范围不一致:
GetTargets中Physics.OverlapSphere的半径是25,而maxLockDist是65,导致距离在25-65之间的敌人无法被检测到,只有移动角色让敌人进入25范围内才会被识别,这就是调整角度偶尔恢复锁定的原因。 - 目标列表未及时清空:
GetTargets每次调用时没有先清空availableTars,导致列表累积之前的旧目标,可能包含已经失效或超出范围的对象,干扰目标判断。 - 未处理目标状态:没有检查目标是否处于激活状态,若目标被销毁或失活,
currentTarget会变为null,但此时可能还有其他可用目标,却直接关闭锁定状态。
修复后的代码
public class PlayerLockOn : MonoBehaviour { public bool lockOn = false; float maxLockDist = 65; List<GameObject> availableTars = new List<GameObject>(); float shortestDist; public GameObject closestLockOnTarget; public GameObject currentTarget; void Update() { if (lockOn) { GetTargets(); ManageTargets(); if (currentTarget == null && availableTars.Count > 0) { // 若当前目标失效,自动选择下一个可用目标 ManageTargets(); } if (currentTarget == null) { CameraMovement.instance.camRecenter(); lockOn = false; } else { PlayerStateManager.instance.camState = 1; CameraMovement.instance.lockOnTarget = currentTarget; } } else { availableTars.Clear(); currentTarget = null; CameraMovement.instance.lockOnTarget = null; closestLockOnTarget = null; PlayerStateManager.instance.camState = 0; } } void GetTargets() { availableTars.Clear(); // 每次获取前清空列表 Collider[] colliders = Physics.OverlapSphere(transform.position, maxLockDist); // 使用maxLockDist作为检测半径 foreach (var collider in colliders) { if (collider.CompareTag("Enemy")) { Transform tarTransform = collider.transform; float distance = Vector3.Distance(tarTransform.position, transform.position); // 排除自身,检查目标是否激活,且在锁定距离内 if (PlayerStateManager.instance.Player.transform.root != tarTransform.root && distance <= maxLockDist && collider.gameObject.activeInHierarchy) { // 添加可见性检测 if (IsTargetVisible(collider.gameObject)) { availableTars.Add(collider.gameObject); } } } } } void ManageTargets() { shortestDist = Mathf.Infinity; // 每次计算前重置最短距离 closestLockOnTarget = null; foreach (var target in availableTars) { if (target == null) continue; // 跳过已失效的目标 float dist = Vector3.Distance(transform.position, target.transform.position); if (dist < shortestDist) { shortestDist = dist; closestLockOnTarget = target; } } currentTarget = closestLockOnTarget; } // 可见性检测:检查玩家到目标之间是否有障碍物 bool IsTargetVisible(GameObject target) { Vector3 direction = target.transform.position - transform.position; RaycastHit hit; // 射线检测,忽略自身和目标的碰撞体 int layerMask = ~(1 << gameObject.layer | 1 << target.layer); if (Physics.Raycast(transform.position, direction.normalized, out hit, maxLockDist, layerMask)) { // 如果射线击中的不是目标,说明被遮挡 return hit.collider.gameObject == target; } // 没有击中任何物体,说明目标可见 return true; } public void lockOnUpdate() { lockOn = !lockOn; // 开启锁定时立即获取目标 if (lockOn) { GetTargets(); ManageTargets(); } } }
关键优化点
- 每次调用
ManageTargets前重置shortestDist为无穷大,确保每次都能正确计算最近目标。 - 统一检测范围为
maxLockDist,避免遗漏远处的敌人。 GetTargets每次调用前清空列表,避免累积无效目标。- 添加
IsTargetVisible方法,通过射线检测判断目标是否被障碍物遮挡,只添加可见的目标到可用列表。 - 增加对目标激活状态的检查,跳过已失效的目标。
- 开启锁定时立即执行目标获取和管理逻辑,避免延迟。
内容的提问来源于stack exchange,提问作者Booling96
相关产品推荐
相关产品推荐

