You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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循环里,哪怕角色视觉上已经到了目标点,循环仍在执行,给人一种“移动速度极慢、一直走不到头”的错觉;后续点击可能因为偶然的浮点数巧合,或者目标位置离角色极近,导致判断条件偶然成立,看起来正常。

另外代码还有两个次要问题:

  1. Move协程内重复做射线检测,若点击后鼠标移动,会获取到新的点击结果,和最初的目标位置不匹配,导致后续收集逻辑出错。
  2. 循环内每次调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:31:04