Unity 2D协程中首次调用角色移动速度过慢问题求助
问题排查:首次调用移动协程速度异常
我做了点击屏幕控制角色移动的功能,把移动逻辑放在Coroutine里,方便到达目标后执行后续操作。现在遇到的问题是:第一次调用协程时角色移动极慢,但之后的点击移动都正常。已经排除了Time.deltaTime的影响(删掉这个乘数问题还是存在)。刚接触Coroutine,怀疑是代码结构问题,求帮忙排查。
原代码
private void ClickObject() { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); RaycastHit2D hit = Physics2D.GetRayIntersection(ray); print("start coroutine"); StopAllCoroutines(); StartCoroutine(Move(hit.transform.position)); } private IEnumerator Move(Vector3 targetPos) { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); RaycastHit2D hit = Physics2D.GetRayIntersection(ray); bool arrived = false; while (arrived == false) { transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime); anim.Play("Walk"); if (Vector3.Distance(transform.position, targetPos) == 0) { print("coroutine phase 1"); arrived = true; } yield return null; } if (arrived == true) { anim.Play("Idle"); print("reached position"); if (hit.collider != null) { print(hit.collider.tag); if (hit.collider.gameObject.GetComponent<Collect>() != null) { Destroy(hit.collider.gameObject); print("destroy"); } } //StopCoroutine("Move"); } }
问题原因
首次移动速度慢的核心问题是浮点数精度导致的到达判断失效:Vector3.Distance(transform.position, targetPos) == 0这个判断几乎永远不会成立——因为物体的位置是浮点数存储,Vector3.MoveTowards每次移动的增量是近似值,角色位置很难完全和目标位置的三个轴数值完全相等。首次调用时,协程会一直卡在while循环里,哪怕角色视觉上已经到了目标点,循环仍在执行,给人一种“移动速度极慢、一直走不到头”的错觉;后续点击可能因为偶然的浮点数巧合,或者目标位置离角色极近,导致判断条件偶然成立,看起来正常。
另外代码还有两个次要问题:
Move协程内重复做射线检测,若点击后鼠标移动,会获取到新的点击结果,和最初的目标位置不匹配,导致后续收集逻辑出错。- 循环内每次调用
anim.Play("Walk")会重置动画,导致行走动画播放不流畅。
修复后的代码
private void ClickObject() { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); RaycastHit2D hit = Physics2D.GetRayIntersection(ray); // 先判断是否点击到有效物体,避免空引用异常 if (hit) { print("start coroutine"); StopAllCoroutines(); // 将目标位置和点击结果一并传给协程 StartCoroutine(Move(hit.transform.position, hit)); } } private IEnumerator Move(Vector3 targetPos, RaycastHit2D hit) { bool arrived = false; // 进入移动状态时仅播放一次行走动画 anim.Play("Walk"); // 使用小阈值判断是否到达,规避浮点数精度问题 while (!arrived) { transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime); // 当距离小于0.01时判定为到达,可根据需求调整阈值 if (Vector3.Distance(transform.position, targetPos) < 0.01f) { // 强制将位置设为目标点,消除微小偏移 transform.position = targetPos; print("coroutine phase 1"); arrived = true; } yield return null; } // 到达后切换为 idle 动画 anim.Play("Idle"); print("reached position"); // 简化收集逻辑的判断 if (hit.collider != null && hit.collider.TryGetComponent(out Collect collectComp)) { print(hit.collider.tag); Destroy(hit.collider.gameObject); print("destroy"); } }
关键修复点
- 用距离阈值判断替代
==0的严格相等判断,解决浮点数精度问题。 - 移除协程内的重复射线检测,直接传递点击结果,保证逻辑一致性。
- 仅在进入移动状态时播放一次行走动画,避免动画重置。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

