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

Unity C#如何用Async实现等待NavMeshAgent到达目标点的功能

Unity多角色任务移动功能问题修复方案

核心错误原因

  • 同步死循环卡主线程:GoTo方法中的while (agent.hasPath) ;是空同步循环,Unity所有逻辑都运行在主线程,你占着主线程不放,NavMeshAgent永远没机会更新路径状态、计算位置,agent.hasPath永远不会变成false,直接卡死导致Unity崩溃。
  • Task实现完全错误:GoTo虽然返回了Task类型,但整个逻辑都是同步执行,最后返回的Task.CompletedTask是已完成状态,await完全没有起到异步等待的作用,逻辑和同步调用没有区别。
  • async void存在隐患:ExecuteTask用async void声明,运行中抛出的异常无法被正常捕获,容易出现隐形逻辑错误或崩溃。

修复方案

核心思路是用非阻塞的方式等待角色到达目标点,将协程和TaskCompletionSource结合,把到达状态转换为Task的完成状态,让await可以正常工作:

1. 重写GoTo方法

private Task GoTo(GameObject building)
{
    TaskCompletionSource<bool> arriveTcs = new TaskCompletionSource<bool>();
    CurrentLocation = "World";
    Debug.Log($"Pawn.cs > {FullName}: Going to {building.name}");
    Vector3 target = building.GetComponent<Building>().EntryNode.transform.position;
    NavMeshAgent agent = GetComponent<NavMeshAgent>();
    agent.destination = target;
    
    // 用协程非阻塞等待到达,不占主线程
    StartCoroutine(WaitForArriveCoroutine(agent, building.name, arriveTcs));
    return arriveTcs.Task;
}

private IEnumerator WaitForArriveCoroutine(NavMeshAgent agent, string buildingName, TaskCompletionSource<bool> tcs)
{
    // 等待路径计算完成
    yield return new WaitUntil(() => agent.hasPath);
    // 等待到达目标点(判断剩余距离小于停止距离且没有待计算路径)
    yield return new WaitUntil(() => agent.remainingDistance <= agent.stoppingDistance && !agent.pathPending);
    
    Debug.Log($"Pawn.cs > {FullName} has arrived at {buildingName}");
    CurrentLocation = buildingName;
    // 标记Task完成,await会继续执行后续逻辑
    tcs.SetResult(true);
}

2. 修改ExecuteTask声明

把async void改为async Task,方便后续异常处理和状态追踪:

public async Task ExecuteTask(string task)
{
    Debug.Log($"{this.FullName} is doing {task}");
    switch (task)
    {
        case "Sleep":
        case "Home":
            if (this.CurrentLocation != this.Home.name) 
                await GoTo(Home);
            break;
        case "Work":
            if (this.CurrentLocation != this.Work.name) 
                await GoTo(Work);
            break;
        default:
            Debug.LogWarning($"Pawn.cs > {this.FullName} caught out of bound task: {task}");
            break;
    }
}

补充说明

  • 如果你的项目引入了UniTask插件,可以不用写协程,直接用await UniTask.WaitUntil(() => 条件)的写法,代码会更简洁。
  • 可以在等待到达的逻辑里加超时判断,避免角色被永久阻挡导致任务一直卡住。

内容的提问来源于stack exchange,提问作者Riptide00

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 02:48:02