Unity实现汽车转向行为seek算法时wayIndex不更新车辆卡首路点问题
问题根因与修正方案
核心故障点
- 路点到达判断逻辑错误:使用
Vector3绝对相等判断不符合浮点运算特性,车辆实际位移几乎不可能完全匹配路点坐标,是wayIndex无法更新、车辆卡在第一个路点的核心原因 - Seek算法实现无效:自定义
seek方法中vel和desiredVel计算逻辑完全一致,返回的转向力恒为零,同时持续累加steering变量会导致速度异常漂移 - 逻辑执行顺序存在越界风险:先自增
wayIndex再判断是否到达最后一个路点,会触发数组下标越界报错 - 初始位置设置矛盾:
Start方法直接将车辆初始位置设置为第一个路点坐标,后续位移逻辑反而会让车辆偏离起始路点,错过触发索引更新的时机
修正后完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Follower : MonoBehaviour { public GameObject vehicle; public float[] speeds = {15.0f,25.0f,35.0f}; public int pathChoice; // 路点到达判定阈值,可根据实际需求调整 public float reachThreshold = 1f; [SerializeField] public Transform[] waypoints; public Transform[] waypoints2; [SerializeField] public float speed; private int wayIndex = 0; private Vector3 target; private Vector3 currentVelocity; // 最大转向力,控制转向灵敏度 public float maxSteerForce = 5f; private void Start(){ int speedIndex = UnityEngine.Random.Range(0, 3); int pathIndex = UnityEngine.Random.Range(0, 2); speed = speeds[speedIndex]; pathChoice = pathIndex; currentVelocity = Vector3.zero; if (pathIndex == 0){ target = waypoints[wayIndex].position; // 初始位置可改为路点组的起始前位置,避免初始就重合 // transform.position = waypoints[wayIndex].transform.position; }else if (pathIndex == 1){ target = waypoints2[wayIndex].position; // transform.position = waypoints2[wayIndex].transform.position; } } private void Update(){ MoveVehicle(); } private void MoveVehicle(){ Transform[] activePath = pathChoice == 0 ? waypoints : waypoints2; if (wayIndex >= activePath.Length) return; target = activePath[wayIndex].position; transform.LookAt(target); if (pathChoice == 0) { // 修正后的Seek逻辑 Vector3 desiredVel = (target - transform.position).normalized * speed; Vector3 steerForce = Vector3.ClampMagnitude(desiredVel - currentVelocity, maxSteerForce); currentVelocity = Vector3.ClampMagnitude(currentVelocity + steerForce * Time.deltaTime, speed); transform.position += currentVelocity * Time.deltaTime; } else { transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime); } // 用距离判断是否到达路点,兼容两种位移逻辑 if (Vector3.Distance(transform.position, target) < reachThreshold) { wayIndex++; // 到达最后一个路点的处理逻辑 if (wayIndex >= activePath.Length) { speed = 0f; if (pathChoice == 1) { vehicle.GetComponent<Follower>().enabled = false; if(vehicle.TryGetComponent(out Collisions col)) col.enabled = false; Destroy(vehicle); } } } } }
关键修改说明
- 把路点到达判断从绝对相等改为距离阈值判断,避免浮点误差导致的触发失败
- 重写了Seek算法逻辑,增加了速度累积和转向力限制,保证转向效果正常
- 调整了逻辑执行顺序,先判断是否到达路点再更新索引,最后统一处理路径结束逻辑,避免数组越界
- 注释掉了初始位置设置为第一个路点的逻辑,避免初始状态就触发索引更新
- 复用了路径判断逻辑,减少重复代码,同时增加了组件获取的安全校验
内容的提问来源于stack exchange,提问作者doozy
相关产品推荐
相关产品推荐

