5.条件操作
5.1 知识点
UniTask 条件操作详解
在实际项目中,常常需要等待多个条件满足后再执行特定操作。UniTask 提供了灵活的条件等待方法,通过组合 WaitUntil
、WhenAll
和 WhenAny
等 API,我们可以轻松实现等待多个异步条件的场景。
同时等待多个小球达到目标位置
在示例中,我们通过 UniTask.WaitUntil
分别监控两个小球的位置,再利用 UniTask.WhenAll
等待所有条件都满足。当两个小球的 x 坐标都大于 1 后,程序输出它们的位置,并分别修改小球的颜色。
async UniTaskVoid WaitForAllBallsToReachPosition(CancellationToken token)
{
// 分别创建两个等待任务,监控 ball1 和 ball2 的 x 坐标
var task1 = UniTask.WaitUntil(() => ball1.transform.position.x > 1, cancellationToken: token);
var task2 = UniTask.WaitUntil(() => ball2.transform.position.x > 1, cancellationToken: token);
// 使用 WhenAll 同时等待两个任务完成
await UniTask.WhenAll(task1, task2);
// 所有等待条件满足后,输出小球位置并修改颜色
Debug.Log($"ball1 位置: {ball1.transform.position.x}, ball2 位置: {ball2.transform.position.x}");
ball1.GetComponent<Renderer>().material.color = Color.blue;
ball2.GetComponent<Renderer>().material.color = Color.red;
}
这种方式适用于需要等待多个并行条件同时成立的场景,如多个动画结束、多个任务完成等。
等待任意一个按钮点击
有时我们希望用户只点击任意一个按钮就能触发下一步操作。在示例中,我们对两个按钮的点击事件分别设置等待条件,并通过 UniTask.WhenAny
来等待任一条件满足,满足后输出提示信息。
async UniTaskVoid WaitForAnyButtonClick(CancellationToken token)
{
// 分别创建等待任务,监控 _isClick1 和 _isClick2 状态
var task1 = UniTask.WaitUntil(() => _isClick1, cancellationToken: token);
var task2 = UniTask.WaitUntil(() => _isClick2, cancellationToken: token);
// 使用 WhenAny 等待任意一个任务完成
await UniTask.WhenAny(task1, task2);
// 当任意一个按钮被点击后,输出提示信息
Debug.Log("一个按钮被点击了");
}
这种方式可以用在用户输入、系统状态变化等需要响应第一个满足条件的场景。
等待所有按钮均被点击
当我们需要等待所有按钮都被点击后再继续执行后续逻辑时,可以使用 UniTask.WhenAll
同时监控所有按钮状态。示例代码中,通过等待两个 WaitUntil
任务都完成,实现了等待所有按钮点击的需求。
async UniTaskVoid WaitForAllButtonsClick(CancellationToken cancellationToken)
{
// 分别创建等待任务,监控 _isClick1 和 _isClick2 状态
var task1 = UniTask.WaitUntil(() => _isClick1, cancellationToken: cancellationToken);
var task2 = UniTask.WaitUntil(() => _isClick2, cancellationToken: cancellationToken);
// 使用 WhenAll 等待所有任务完成
await UniTask.WhenAll(task1, task2);
// 当所有按钮都被点击后,输出提示信息
Debug.Log("所有按钮都被点击了");
}
通过这种组合使用,UniTask 让条件等待操作变得简单直观,同时避免了传统协程中繁琐的状态管理。
进行测试
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Threading;
/// <summary>
/// 使用 UniTask 实现条件等待操作的示例
/// </summary>
public class Lesson05_条件操作 : MonoBehaviour
{
public GameObject ball1; // 第一个小球对象
public GameObject ball2; // 第二个小球对象
public Button btn1; // 第一个按钮
public Button btn2; // 第二个按钮
private bool _isClick1 = false; // 标记第一个按钮是否被点击
private bool _isClick2 = false; // 标记第二个按钮是否被点击
private CancellationTokenSource _cancellationToken; // 用于取消异步任务的令牌源
void Start()
{
_cancellationToken = new CancellationTokenSource();
// 注册按钮点击事件
btn1.onClick.AddListener(() => _isClick1 = true);
btn2.onClick.AddListener(() => _isClick2 = true);
// 启动条件等待任务
WaitForAllBallsToReachPosition(_cancellationToken.Token).Forget();
WaitForAnyButtonClick(_cancellationToken.Token).Forget();
WaitForAllButtonsClick(_cancellationToken.Token).Forget();
}
void Update()
{
// 移动 ball1,速度为每秒 1 单位
ball1.transform.Translate(Vector3.right * Time.deltaTime);
// 移动 ball2,速度为每秒 0.5 单位
ball2.transform.Translate(Vector3.right * Time.deltaTime * 0.5f);
}
/// <summary>
/// 等待所有小球的 x 坐标都超过 1,然后输出它们的位置
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitForAllBallsToReachPosition(CancellationToken token)
{
// 创建等待任务
var task1 = UniTask.WaitUntil(() => ball1.transform.position.x > 1, cancellationToken: token);
var task2 = UniTask.WaitUntil(() => ball2.transform.position.x > 1, cancellationToken: token);
// 等待所有任务完成
await UniTask.WhenAll(task1, task2);
// 输出小球位置
Debug.Log($"ball1 位置: {ball1.transform.position.x}, ball2 位置: {ball2.transform.position.x}");
ball1.GetComponent<Renderer>().material.color = Color.blue;
ball2.GetComponent<Renderer>().material.color = Color.red;
}
/// <summary>
/// 等待任意一个按钮被点击,然后输出提示信息
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitForAnyButtonClick(CancellationToken token)
{
// 创建等待任务
var task1 = UniTask.WaitUntil(() => _isClick1, cancellationToken: token);
var task2 = UniTask.WaitUntil(() => _isClick2, cancellationToken: token);
// 等待任意一个任务完成
await UniTask.WhenAny(task1, task2);
// 输出提示信息
Debug.Log("一个按钮被点击了");
}
/// <summary>
/// 等待所有按钮都被点击,然后输出提示信息
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
async UniTaskVoid WaitForAllButtonsClick(CancellationToken cancellationToken)
{
// 创建等待任务
var task1 = UniTask.WaitUntil(() => _isClick1, cancellationToken: cancellationToken);
var task2 = UniTask.WaitUntil(() => _isClick2, cancellationToken: cancellationToken);
// 等待所有任务完成
await UniTask.WhenAll(task1, task2);
// 输出提示信息
Debug.Log("所有按钮都被点击了");
}
private void OnDestroy()
{
// 在对象销毁时取消所有异步任务
_cancellationToken.Cancel();
_cancellationToken.Dispose();
}
}
总结
本文详细介绍了 UniTask 在条件等待场景下的应用,包括同时等待多个条件(WhenAll
)、等待任一条件(WhenAny
)以及基于条件的等待操作。通过这些 API,我们可以编写出更加简洁、易于维护的异步逻辑,大幅提升 Unity 开发中的交互响应能力。
5.2 知识点代码
Lesson05_条件操作.cs
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Threading;
/// <summary>
/// 使用 UniTask 实现条件等待操作的示例
/// </summary>
public class Lesson05_条件操作 : MonoBehaviour
{
public GameObject ball1; // 第一个小球对象
public GameObject ball2; // 第二个小球对象
public Button btn1; // 第一个按钮
public Button btn2; // 第二个按钮
private bool _isClick1 = false; // 标记第一个按钮是否被点击
private bool _isClick2 = false; // 标记第二个按钮是否被点击
private CancellationTokenSource _cancellationToken; // 用于取消异步任务的令牌源
void Start()
{
_cancellationToken = new CancellationTokenSource();
// 注册按钮点击事件
btn1.onClick.AddListener(() => _isClick1 = true);
btn2.onClick.AddListener(() => _isClick2 = true);
// 启动条件等待任务
WaitForAllBallsToReachPosition(_cancellationToken.Token).Forget();
WaitForAnyButtonClick(_cancellationToken.Token).Forget();
WaitForAllButtonsClick(_cancellationToken.Token).Forget();
}
void Update()
{
// 移动 ball1,速度为每秒 1 单位
ball1.transform.Translate(Vector3.right * Time.deltaTime);
// 移动 ball2,速度为每秒 0.5 单位
ball2.transform.Translate(Vector3.right * Time.deltaTime * 0.5f);
}
/// <summary>
/// 等待所有小球的 x 坐标都超过 1,然后输出它们的位置
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitForAllBallsToReachPosition(CancellationToken token)
{
// 创建等待任务
var task1 = UniTask.WaitUntil(() => ball1.transform.position.x > 1, cancellationToken: token);
var task2 = UniTask.WaitUntil(() => ball2.transform.position.x > 1, cancellationToken: token);
// 等待所有任务完成
await UniTask.WhenAll(task1, task2);
// 输出小球位置
Debug.Log($"ball1 位置: {ball1.transform.position.x}, ball2 位置: {ball2.transform.position.x}");
ball1.GetComponent<Renderer>().material.color = Color.blue;
ball2.GetComponent<Renderer>().material.color = Color.red;
}
/// <summary>
/// 等待任意一个按钮被点击,然后输出提示信息
/// </summary>
/// <param name="token">取消令牌</param>
async UniTaskVoid WaitForAnyButtonClick(CancellationToken token)
{
// 创建等待任务
var task1 = UniTask.WaitUntil(() => _isClick1, cancellationToken: token);
var task2 = UniTask.WaitUntil(() => _isClick2, cancellationToken: token);
// 等待任意一个任务完成
await UniTask.WhenAny(task1, task2);
// 输出提示信息
Debug.Log("一个按钮被点击了");
}
/// <summary>
/// 等待所有按钮都被点击,然后输出提示信息
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
async UniTaskVoid WaitForAllButtonsClick(CancellationToken cancellationToken)
{
// 创建等待任务
var task1 = UniTask.WaitUntil(() => _isClick1, cancellationToken: cancellationToken);
var task2 = UniTask.WaitUntil(() => _isClick2, cancellationToken: cancellationToken);
// 等待所有任务完成
await UniTask.WhenAll(task1, task2);
// 输出提示信息
Debug.Log("所有按钮都被点击了");
}
private void OnDestroy()
{
// 在对象销毁时取消所有异步任务
_cancellationToken.Cancel();
_cancellationToken.Dispose();
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com