4.等待操作
4.1 知识点
UniTask 等待操作详解
在 Unity 开发中,处理异步操作时,常需要等待特定条件满足后再继续执行后续代码。UniTask 提供了高效的等待方法,如 UniTask.WaitUntil
和 UniTask.WaitUntilValueChanged
,使得异步代码的编写更加简洁和高效。本文将通过具体示例,详细介绍这些等待操作的用法和注意事项。
UniTask.WaitUntil:等待条件满足
UniTask.WaitUntil
用于等待直到指定的条件返回 true
,然后继续执行后续代码。
using Cysharp.Threading.Tasks;
using UnityEngine;
using System.Threading;
public class WaitUntilExample : MonoBehaviour
{
public GameObject ball;
private CancellationTokenSource _cancellationTokenSource;
void Start()
{
_cancellationTokenSource = new CancellationTokenSource();
WaitUntilPositionExceeds(_cancellationTokenSource.Token).Forget();
}
async UniTaskVoid WaitUntilPositionExceeds(CancellationToken token)
{
Debug.Log($"等待开始: {Time.time}");
await UniTask.WaitUntil(() => ball.transform.position.x > 1, cancellationToken: token);
Debug.Log($"等待结束: {Time.time}");
ball.GetComponent<Renderer>().material.color = Color.red;
}
private void OnDestroy()
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
在上述示例中,WaitUntilPositionExceeds
方法会等待直到 ball
的 x 坐标大于 1,然后将其颜色更改为红色。UniTask.WaitUntil
方法接受一个返回布尔值的委托,当该委托返回 true
时,等待结束,继续执行后续代码。
UniTask.WaitUntilValueChanged:等待值发生变化
UniTask.WaitUntilValueChanged
用于等待某个值发生变化,然后继续执行后续代码。
using Cysharp.Threading.Tasks;
using UnityEngine;
using System.Threading;
public class WaitUntilValueChangedExample : MonoBehaviour
{
public GameObject ball;
private CancellationTokenSource _cancellationTokenSource;
void Start()
{
_cancellationTokenSource = new CancellationTokenSource();
WaitUntilPositionChanges(_cancellationTokenSource.Token).Forget();
}
async UniTaskVoid WaitUntilPositionChanges(CancellationToken token)
{
await UniTask.WaitUntilValueChanged(ball.transform, x => x.position, cancellationToken: token);
Debug.Log("小球位置已变化");
}
private void OnDestroy()
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
在上述示例中,WaitUntilPositionChanges
方法会等待直到 ball
的位置发生变化,然后输出日志信息。UniTask.WaitUntilValueChanged
方法接受一个对象和一个用于监视该对象某个属性或字段的委托,当该属性或字段的值发生变化时,等待结束,继续执行后续代码。
使用 CancellationToken 取消等待操作
在异步操作中,使用 CancellationToken
可以在需要时取消等待操作,防止出现无限等待的情况。
using Cysharp.Threading.Tasks;
using UnityEngine;
using System.Threading;
public class CancellationExample : MonoBehaviour
{
private CancellationTokenSource _cancellationTokenSource;
void Start()
{
_cancellationTokenSource = new CancellationTokenSource();
PerformCancelableTask(_cancellationTokenSource.Token).Forget();
}
async UniTaskVoid PerformCancelableTask(CancellationToken token)
{
try
{
await UniTask.Delay(5000, cancellationToken: token);
Debug.Log("任务完成");
}
catch (OperationCanceledException)
{
Debug.Log("任务被取消");
}
}
void OnDestroy()
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
在上述示例中,PerformCancelableTask
方法会等待 5 秒钟,然后输出“任务完成”。如果在这 5 秒内对象被销毁,OnDestroy
方法会取消该任务,并输出“任务被取消”。
进行测试
using UnityEngine;
using Cysharp.Threading.Tasks;
using System.Threading;
/// <summary>
/// 使用 UniTask 实现等待操作的示例
/// </summary>
public class Lesson04_等待操作 : MonoBehaviour
{
public GameObject ball; // 要操作的小球对象
private CancellationTokenSource _cancellationTokenSource; // 用于取消异步任务的令牌源
private Vector3 _moveDirection = Vector3.right; // 小球移动方向,初始向右
void Start()
{
// 初始化令牌源
_cancellationTokenSource = new CancellationTokenSource();
// 启动等待小球移动超过指定位置的任务
WaitUntilPositionExceeds(_cancellationTokenSource.Token).Forget();
// 启动等待小球位置变化的任务
WaitUntilPositionChanges(_cancellationTokenSource.Token).Forget();
}
void Update()
{
// 按当前移动方向移动小球
ball.transform.Translate(_moveDirection * Time.deltaTime);
// 当小球移动到 x > 2 时,改变方向向左
if (ball.transform.position.x > 2)
{
_moveDirection = Vector3.left;
}
// 当小球移动到 x < -2 时,改变方向向右
else if (ball.transform.position.x < -2)
{
_moveDirection = Vector3.right;
}
}
/// <summary>
/// 等待小球的 x 坐标超过 1,然后改变其颜色
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitUntilPositionExceeds(CancellationToken token)
{
Debug.Log("等待开始: " + Time.time);
// 等待直到小球的 x 坐标超过 1
await UniTask.WaitUntil(() => ball.transform.position.x > 1, cancellationToken: token);
Debug.Log("等待结束: " + Time.time);
// 将小球颜色改为红色
ball.GetComponent<Renderer>().material.color = Color.red;
}
/// <summary>
/// 等待小球的位置发生变化
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitUntilPositionChanges(CancellationToken token)
{
// 等待直到小球的位置发生变化
await UniTask.WaitUntilValueChanged(ball.transform, x => x.position, cancellationToken: token);
Debug.Log("小球位置已变化");
}
private void OnDestroy()
{
// 在对象销毁时取消所有异步任务
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
总结
UniTask 提供了丰富的等待操作方法,如 UniTask.WaitUntil
和 UniTask.WaitUntilValueChanged
,能够高效地替代传统的协程,实现更简洁和性能优化的异步代码。在使用这些方法时,结合 CancellationToken
可以更好地控制异步任务的生命周期,避免不必要的资源消耗。
4.2 知识点代码
Lesson04_等待操作.cs
using UnityEngine;
using Cysharp.Threading.Tasks;
using System.Threading;
/// <summary>
/// 使用 UniTask 实现等待操作的示例
/// </summary>
public class Lesson04_等待操作 : MonoBehaviour
{
public GameObject ball; // 要操作的小球对象
private CancellationTokenSource _cancellationTokenSource; // 用于取消异步任务的令牌源
private Vector3 _moveDirection = Vector3.right; // 小球移动方向,初始向右
void Start()
{
// 初始化令牌源
_cancellationTokenSource = new CancellationTokenSource();
// 启动等待小球移动超过指定位置的任务
WaitUntilPositionExceeds(_cancellationTokenSource.Token).Forget();
// 启动等待小球位置变化的任务
WaitUntilPositionChanges(_cancellationTokenSource.Token).Forget();
}
void Update()
{
// 按当前移动方向移动小球
ball.transform.Translate(_moveDirection * Time.deltaTime);
// 当小球移动到 x > 2 时,改变方向向左
if (ball.transform.position.x > 2)
{
_moveDirection = Vector3.left;
}
// 当小球移动到 x < -2 时,改变方向向右
else if (ball.transform.position.x < -2)
{
_moveDirection = Vector3.right;
}
}
/// <summary>
/// 等待小球的 x 坐标超过 1,然后改变其颜色
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitUntilPositionExceeds(CancellationToken token)
{
Debug.Log("等待开始: " + Time.time);
// 等待直到小球的 x 坐标超过 1
await UniTask.WaitUntil(() => ball.transform.position.x > 1, cancellationToken: token);
Debug.Log("等待结束: " + Time.time);
// 将小球颜色改为红色
ball.GetComponent<Renderer>().material.color = Color.red;
}
/// <summary>
/// 等待小球的位置发生变化
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitUntilPositionChanges(CancellationToken token)
{
// 等待直到小球的位置发生变化
await UniTask.WaitUntilValueChanged(ball.transform, x => x.position, cancellationToken: token);
Debug.Log("小球位置已变化");
}
private void OnDestroy()
{
// 在对象销毁时取消所有异步任务
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com