0x01.UniTask的Wait*函数示例
以下是 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)适合状态监听; - 时间/帧等待适合定时或逐帧逻辑;
- 对象生命周期等待适合监听物体状态;
- 多任务等待适合并行操作协同;
- 属性变化等待适合数据驱动的场景。
根据具体需求选择对应方法,可大幅简化异步逻辑的编写。