以下是 UniTask 中各类 Wait 方法的分类总结 + 代码示例,涵盖所有常用场景,方便直接参考使用:


一、条件等待(基于布尔条件)

1. WaitUntil

功能:等待条件为 true 时继续
示例:等待玩家分数达到 100 分

private int playerScore = 0;

private async void CheckScore()
{
    Debug.Log("等待分数达到100...");
    // 条件:分数 >= 100 时结束等待
    await UniTask.WaitUntil(() => playerScore >= 100);
    Debug.Log("分数达标!");
}

// 模拟加分(调用后分数变化)
public void AddScore(int score)
{
    playerScore += score;
    Debug.Log($"当前分数:{playerScore}");
}

2. WaitWhile

功能:等待条件为 false 时继续
示例:等待敌人存活状态结束(敌人死亡后继续)

private bool enemyIsAlive = true;

private async void WaitEnemyDeath()
{
    Debug.Log("等待敌人死亡...");
    // 条件:敌人存活时持续等待,死亡后结束
    await UniTask.WaitWhile(() => enemyIsAlive);
    Debug.Log("敌人已死亡!");
}

// 模拟敌人死亡
public void EnemyDie()
{
    enemyIsAlive = false;
}

二、时间等待(基于时间长度)

1. UniTask.Delay

功能:等待指定毫秒数(支持实时/缩放时间)
示例:等待 2 秒(受时间缩放影响)和 3 秒真实时间(不受暂停影响)

private async void WaitByDelay()
{
    // 等待 2 秒(Time.timeScale 为 0 时会暂停)
    await UniTask.Delay(2000); // 2000毫秒 = 2秒
    Debug.Log("2秒后执行(受时间缩放影响)");

    // 等待 3 秒真实时间(即使暂停也会计时)
    await UniTask.Delay(3000, DelayType.RealTime);
    Debug.Log("3秒真实时间后执行(不受暂停影响)");
}

2. UniTask.WaitForSeconds

功能:等待指定秒数(受时间缩放影响,类似原生 WaitForSeconds
示例:等待 1.5 秒后执行

private async void WaitBySeconds()
{
    Debug.Log("开始等待...");
    await UniTask.WaitForSeconds(1.5f); // 等待1.5秒
    Debug.Log("1.5秒后执行");
}

三、帧与更新时机等待(基于游戏循环)

1. UniTask.Yield

功能:等待当前帧结束,下一帧继续(每帧执行一次)
示例:每帧打印一次,共执行 5 帧

private async void LoopPerFrame()
{
    for (int i = 0; i < 5; i++)
    {
        Debug.Log($"第 {i} 帧(当前帧)");
        await UniTask.Yield(); // 等待当前帧结束,下一帧继续循环
    }
}

2. UniTask.WaitForEndOfFrame

功能:等待当前帧渲染结束(适合截图等操作)
示例:帧末执行截图

private async void CaptureScreen()
{
    Debug.Log("帧开始,准备截图...");
    await UniTask.WaitForEndOfFrame(); // 等待帧渲染结束
    ScreenCapture.CaptureScreenshot("screenshot.png");
    Debug.Log("截图完成(帧末执行)");
}

3. UniTask.WaitForFixedUpdate

功能:等待下一次 FixedUpdate(适合物理逻辑)
示例:在 FixedUpdate 中给刚体加力

private Rigidbody rb;

private async void AddForceInFixedUpdate()
{
    rb = GetComponent<Rigidbody>();
    await UniTask.WaitForFixedUpdate(); // 等待下一次FixedUpdate
    rb.AddForce(Vector3.up * 10f); // 物理操作在FixedUpdate中执行更稳定
}

四、对象/组件生命周期等待

1. UniTask.WaitUntilDestroyed

功能:等待目标对象被销毁后继续
示例:监听道具对象销毁

public GameObject item; // 场景中的道具

private async void WaitItemDestroy()
{
    Debug.Log("等待道具被销毁...");
    await UniTask.WaitUntilDestroyed(item); // 等待item被销毁
    Debug.Log("道具已消失!");
}

// 调用此方法销毁道具
public void DestroyItem()
{
    Destroy(item);
}

2. UniTask.WaitWhileActive / UniTask.WaitUntilActive

功能:等待对象激活状态变化(激活 → 禁用 / 禁用 → 激活)
示例:等待面板从激活变为禁用

public GameObject panel; // UI面板

private async void WaitPanelClose()
{
    Debug.Log("等待面板关闭...");
    // 面板激活时持续等待,禁用后继续
    await UniTask.WaitWhileActive(panel);
    Debug.Log("面板已关闭");
}

// 调用此方法关闭面板
public void ClosePanel()
{
    panel.SetActive(false);
}

五、多任务等待(基于多个异步操作)

1. UniTask.WhenAll

功能:等待多个任务全部完成后继续
示例:并行加载两个资源,全部完成后处理

private async void LoadMultipleAssets()
{
    // 启动两个并行加载任务
    var loadTexture = LoadAsset<Texture2D>("icon");
    var loadAudio = LoadAsset<AudioClip>("bgm");

    // 等待两个任务都完成
    await UniTask.WhenAll(loadTexture, loadAudio);

    // 处理结果
    Debug.Log("所有资源加载完成:");
    Debug.Log($"纹理:{loadTexture.Result.name}");
    Debug.Log($"音频:{loadAudio.Result.name}");
}

// 模拟资源加载
private async UniTask<T> LoadAsset<T>(string path) where T : UnityEngine.Object
{
    await UniTask.Delay(1000); // 模拟加载耗时
    return new T(); // 实际项目中返回加载的资源
}

2. UniTask.WhenAny

功能:等待多个任务中任意一个完成后继续
示例:从两个下载源取最快的结果

private async void DownloadFastest()
{
    // 两个并行下载任务
    var downloadA = DownloadFile("url_A");
    var downloadB = DownloadFile("url_B");

    // 等待任意一个完成
    var completedTask = await UniTask.WhenAny(downloadA, downloadB);

    Debug.Log($"最快完成的任务:{completedTask.Result}");
}

// 模拟文件下载
private async UniTask<string> DownloadFile(string url)
{
    // 随机模拟下载耗时(1-3秒)
    var delay = Random.Range(1000, 3000);
    await UniTask.Delay(delay);
    return url;
}

六、属性变化等待

UniTask.WaitForFirstValueChanged

功能:等待目标对象的指定属性首次变化
示例:监听玩家生命值变化

public class Player : MonoBehaviour, INotifyPropertyChanged
{
    private int _hp;
    public int Hp 
    { 
        get => _hp; 
        set 
        { 
            _hp = value; 
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hp)));
        } 
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

// 监听Hp变化
private async void ListenHpChange()
{
    var player = GetComponent<Player>();
    Debug.Log("等待生命值变化...");
    // 等待Hp首次变化
    await UniTask.WaitForFirstValueChanged(player, p => p.Hp);
    Debug.Log($"生命值已变化:{player.Hp}");
}

总结

通过这些示例可以直观看到:

  • 条件等待(WaitUntil/WaitWhile)适合状态监听;
  • 时间/帧等待适合定时或逐帧逻辑;
  • 对象生命周期等待适合监听物体状态;
  • 多任务等待适合并行操作协同;
  • 属性变化等待适合数据驱动的场景。

根据具体需求选择对应方法,可大幅简化异步逻辑的编写。