5.UniTask条件操作

5.条件操作


5.1 知识点

UniTask 条件操作详解

在实际项目中,常常需要等待多个条件满足后再执行特定操作。UniTask 提供了灵活的条件等待方法,通过组合 WaitUntilWhenAllWhenAny 等 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

×

喜欢就点赞,疼爱就打赏