UniTask 异步任务

news2024/12/27 0:02:04

文章目录

  • 前言
  • 一、UniTask是什么?
  • 二、使用步骤
  • 三、常用的UniTask API和示例
    • 1.编写异步方法
    • 2.处理异常
    • 3.延迟执行
    • 4.等待多个UniTask或者一个UniTas完成
    • 5.异步加载资源示例
    • 6.手动控制UniTask的完成状态
    • 7.UniTask.Lazy延迟任务的创建
    • 8.后台线程切换Unity主线程
    • 9.不要返回值
    • 10.缓存UniTask的结果
    • 11.指定异步操作阶段执行
    • 12.创建完成的UniTask
    • 13.UniTask.ToCoroutine转换协程
    • 14.异步迭代集合
    • 15. 异步判断
    • 16. uniTask的取消
    • 17.网络请求加载图片
    • 18.异步判断按钮是否被双击
    • 19.每次点击修改按钮不同的文本
  • 四、 总结

前言

随着Unity游戏开发的流行,异步编程变得越来越重要。UniTask是一个轻量级的异步编程库,它为Unity开发者提供了更高效的编程方式。本文将介绍UniTask的基础内容和一些常用的API,以及它们的使用示例。


一、UniTask是什么?

UniTask是一个专为Unity设计的异步编程库,它提供了类似于C#的Taskasync/await的功能,但是更加适合Unity的执行模型和生命周期。

二、使用步骤

UniTaskGit地址
UniTask中文文档地址

首先,需要在Unity项目中安装UniTask。可以通过Unity Package Manager(UPM)安装,或者直接从GitHub下载源码

本文介绍从GitHub下载
在这里插入图片描述

在这里插入图片描述


三、常用的UniTask API和示例

1.编写异步方法

使用UniTask时,可以像编写普通的异步方法一样使用async关键字和UniTask返回类型

// 示例:一个简单的异步加载场景的方法
async UniTask LoadSceneAsync(string sceneName)
{
    // 使用UniTask的场景加载方法
    await SceneManager.LoadSceneAsync(sceneName).ToUniTask();
    // 场景加载完成后的操作
    Debug.Log("Scene loaded");
}

2.处理异常

UniTask也支持异常处理,可以使用try/catch块来捕获异步方法中的异常

// 示例:带有异常处理的异步方法
async UniTask DoSomethingAsync()
{
    try
    {
        // 可能会抛出异常的操作
        await SomeRiskyOperation().ToUniTask();
    }
    catch (Exception ex)
    {
        // 处理异常
        Debug.LogError(ex.Message);
    }
}

3.延迟执行

UniTask.Delay: 延迟执行,类似于Task.Delay

// 延迟执行示例
async UniTask DelayedOperation()
{
    await UniTask.Delay(TimeSpan.FromSeconds(5));
    Debug.Log("Operation executed after 5 seconds");
}

4.等待多个UniTask或者一个UniTas完成

UniTask.WhenAll: 等待多个UniTask完成,类似于Task.WhenAll

// 并行执行多个任务示例
async UniTask RunMultipleTasks()
{
    // 创建多个任务
    var task1 = DoTask1Async();
    var task2 = DoTask2Async();
    
    // 等待所有任务完成
    await UniTask.WhenAll(task1, task2);
    
    Debug.Log("全部任务完成");
}
async void RunSingleTask()
{
    UniTask task1 = UniTask.WaitUntil(() => isClick1);
    UniTask task2 = UniTask.WaitUntil(() => isClick2);
    await UniTask.WhenAny(task1, task2);
    Debug.Log("一个任务完成");
}

5.异步加载资源示例

// 异步加载资源示例
async UniTask<Texture> LoadTextureAsync(string url)
{
    using (var request = UnityWebRequestTexture.GetTexture(url))
    {
        await request.SendWebRequest().ToUniTask();
        if (request.isNetworkError || request.isHttpError)
        {
            Debug.LogError(request.error);
            return null;
        }
        return DownloadHandlerTexture.GetContent(request);
    }
}

6.手动控制UniTask的完成状态

UniTaskCompletionSource允许你手动控制UniTask的完成状态。这在需要等待非异步方法完成的情况下非常有用。

UniTaskCompletionSource<bool> ucs = new UniTaskCompletionSource<bool>();

// 在某个事件发生时完成任务
void OnSomeEvent()
{
    ucs.TrySetResult(true);
}

// 等待事件完成
async UniTask WaitSomeEventAsync()
{
    await ucs.Task;
    Debug.Log("Event occurred");
}

7.UniTask.Lazy延迟任务的创建

UniTask.Lazy可以延迟任务的创建,直到真正需要执行任务时才会创建。

// 示例:延迟创建任务
UniTask<string> lazyTask = UniTask.Lazy(async () =>
{
    await UniTask.Delay(1000);
    return "Result after delay";
});

// 在需要结果时等待任务
string result = await lazyTask;
Debug.Log(result);

8.后台线程切换Unity主线程

在需要从后台线程切换回Unity主线程时,可以使用UniTask.SwitchToMainThread

// 示例:在后台线程上执行操作,然后切换回主线程
async UniTask DoOperationAndSwitchBack()
{
    // 在后台线程上执行耗时操作
    await UniTask.Run(() => SomeHeavyOperation());
    
    // 切换回主线程
    await UniTask.SwitchToMainThread();
    
    // 在主线程上执行Unity相关操作
    Debug.Log("Back on main thread");
}

9.不要返回值

当你不需要关心异步方法的返回值时,可以使用UniTaskVoid。这对于触发事件或执行不需要返回结果的后台操作非常有用。

// 示例:一个不返回任何结果的异步方法
async UniTaskVoid PerformBackgroundOperation()
{
    // 执行一些后台操作
    await UniTask.Delay(1000);
    Debug.Log("Background operation completed");
}

10.缓存UniTask的结果

如果你想要缓存一个UniTask的结果以供后续使用,可以使用UniTask<T>.Preserve方法。

// 示例:缓存异步操作的结果
UniTask<int> originalTask = CalculateValueAsync();
UniTask<int> preservedTask = originalTask.Preserve();

// 在不同的地方使用缓存的结果
int result1 = await preservedTask;
int result2 = await preservedTask;

11.指定异步操作阶段执行

PlayerLoopTiming枚举允许你指定异步操作应该在Unity的哪个阶段执行。这可以帮助你更精确地控制代码的执行时间。

// 示例:在FixedUpdate阶段执行异步操作
async UniTask PerformOperationInFixedUpdate()
{
    await UniTask.Yield(PlayerLoopTiming.FixedUpdate);
    // 在FixedUpdate阶段执行的代码
    Debug.Log("This is executed in FixedUpdate");
}

12.创建完成的UniTask

当你已经有了结果,并且想要创建一个已经完成的UniTask时,可以使用UniTask.FromResult

// 示例:创建一个已经完成的UniTask
UniTask<int> completedTask = UniTask.FromResult(42);

13.UniTask.ToCoroutine转换协程

如果你需要将UniTask与旧的协程系统兼容,可以使用UniTask.ToCoroutine将其转换为协程。

// 示例:将UniTask转换为协程
IEnumerator MyCoroutine()
{
    yield return LoadSceneAsync("MyScene").ToCoroutine();
}

14.异步迭代集合

UniTask支持异步枚举器,这允许你使用await foreach来异步迭代集合。

// 示例:使用异步枚举器迭代集合
async UniTask ProcessItemsAsync(IAsyncEnumerable<int> items)
{
    await foreach (var item in items)
    {
        Debug.Log(item);
    }
}

15. 异步判断

UniTask.WaitUntilUniTask.WaitWhile这些方法允许你等待直到某个条件为真或为假。

// 示例:等待直到条件为真
async UniTask WaitUntilConditionMet()
{
    await UniTask.WaitUntil(() => someCondition);
    Debug.Log("Condition met");
}

// 示例:等待直到条件为假
async UniTask WaitWhileConditionMet()
{
    await UniTask.WaitWhile(() => someCondition);
    Debug.Log("Condition no longer met");
}

16. uniTask的取消

通过 CancellationTokenSource完成UniTask的取消


public class Test : MonoBehaviour
{
    private CancellationTokenSource cts;


    void Start()
    {
        cts = new CancellationTokenSource();

        StartCount();

        cts.CancelAfter(TimeSpan.FromSeconds(10));
    }

    private async UniTask<int> Count(int count, CancellationToken token)
    {
        for (int i = 0; i < count; i++)
        {
            Debug.Log(i);
            await UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: token);
        }

        return 0;
    }

    private async void StartCount()
    {
        try
        {
            await Count(100, cts.Token);
        }
        catch (OperationCanceledException)
        {
            Debug.Log("计数取消");
        }
    }
}

17.网络请求加载图片

private async UniTaskVoid UnityTaskTest()
{
    var webRequest = UnityWebRequestTexture.GetTexture(url);
    var result = await webRequest.SendWebRequest();
    var texture = ((DownloadHandlerTexture)webRequest.downloadHandler).texture;
    Sprite sprite = Sprite.Create(texture,
            new Rect(Vector2.zero, new Vector2(texture.width, texture.height)), new Vector2(0.5f, 0.5f));
    image2.sprite = sprite;

}

18.异步判断按钮是否被双击

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public Button btn1;

    void Start()
    {
        CheckClickInternal(this.GetCancellationTokenOnDestroy(), 1).Forget();
    }


    /// <summary>
    /// 异步判断按钮是否被双击
    /// </summary>
    /// <param name="token"></param>
    private async UniTaskVoid CheckClickInternal(CancellationToken token, float time)
    {
        while (true)
        {
            var firstClick = btn1.OnClickAsync(token);
            await firstClick;
            var secondClick = btn1.OnClickAsync(token);

            //判断第二次点击和等待time时间谁先执行,如果是secondClick返回0,如果不是返回1
            int index = await UniTask.WhenAny(secondClick,
                UniTask.Delay(TimeSpan.FromSeconds(time), cancellationToken: token));
            if (index == 0)
            {
                Debug.Log("被双击了");
            }
        }
    }
}

19.每次点击修改按钮不同的文本

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public Button btn1;

    // Start is called before the first frame update
    void Start()
    {
        btn1.GetComponentInChildren<Text>().text = "杀";
        ChangeBtnTextClick(this.GetCancellationTokenOnDestroy()).Forget();
    }


    /// <summary>
    /// 每次点击修改按钮不同的文本;
    /// </summary>
    /// <param name="token"></param>
    async UniTask ChangeBtnTextClick(CancellationToken token)
    {
        var click = btn1.OnClickAsAsyncEnumerable();
        await click.Take(3).ForEachAsync((_, index) =>
        {
            if (token.IsCancellationRequested) return;
            btn1.GetComponentInChildren<Text>().text = index switch
            {
                0 => "再杀",
                1 => "再补一刀",
                _ => ""
            };
        }, cancellationToken: token);
        btn1.GetComponentInChildren<Text>().text = "结束";
    }
}

四、 总结

Unitask 是一个优秀的异步任务库,它简化了在 Unity 中处理异步操作的方式,提高了开发效率和代码可维护性。通过本文的介绍,可以更加深入地了解 Unitask 的使用方法,并在自己的项目中应用它,从而提升游戏开发的质量和效率。

在使用 Unitask 时,需要注意以下几点:

​ 不要在 Update 方法中使用 async/await,因为 Unity 主线程是单线程的,使用异步方法可能导致性能问题。

​ 尽量避免过多的异步嵌套,以免导致代码复杂度过高。

​ 使用 try/catch 来处理异步操作中的错误,确保程序的稳定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1532766.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java数据结构与算法刷题-----LeetCode406. 根据身高重建队列

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 从高到底排序 1. 从高到底排序 解题思路&#xff1a;时间复杂…

MCU技术的创新浪潮与产业变革

MCU技术的创新浪潮与产业变革 一、MCU技术的创新发展 MCU&#xff0c;即微控制器&#xff0c;作为现代电子设备的核心部件&#xff0c;一直在不断地创新与发展。随着科技的进步&#xff0c;MCU的性能得到了极大的提升&#xff0c;功能也越来越丰富。从8位到32位&#xff0c;再…

MYSQL数据库管理基本操作

一、数据库的基本操作 1、登录数据库 [rootmysql-server ~]#mysql -uroot -p123456 ###直接回车&#xff0c;则进入数据库[rootmysql-server ~]#mysql -u root -p ###直接回车 Enter password: ###输入密码 方法一&#xff1a…

OpenGL学习笔记【2】——开发环境配置(GLFW,VS,Cmake),创建第一个项目

学OpenGL的都会知道&#xff0c;OpenGL只提供了绘图功能&#xff0c;创建窗口是需要自己完成的。这就需要学习相应操作系统的创建窗口方法&#xff0c;为简化创建窗口的过程&#xff0c;可以使用专门的窗口库&#xff0c;例如GLFW。使用GLFW之前需要先进行配置&#xff0c;那怎…

SQLiteC/C++接口详细介绍sqlite3_stmt类(四)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;三&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;五&#xff09; 7. sqlite3_bind_parameter_count函数 sqlite3_bind_param…

章节10实验--Ubuntu18.04 Qt MySQL libqsqlmysql.so

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容&#xff0c;所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者&#xff1a;房胜、李旭健、黄…

软考高级:结构化需求分析概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

通过jsDelivr实现Github的图床CDN加速

最近小伙伴们是否发现访问我的个人博客http://xiejava.ishareread.com/图片显示特别快了&#xff1f; 我的博客的图片是放在github上的&#xff0c;众所周知的原因&#xff0c;github访问不是很快&#xff0c;尤其是hexo博客用github做图床经常图片刷不出来。一直想换图床&…

构建一个前端智能停车可视化系统

引言 随着城市化进程的加速&#xff0c;停车难问题日益突出。智能停车可视化系统通过实时展示停车场的车位信息&#xff0c;帮助用户快速找到空闲车位&#xff0c;提高停车效率。 目录 引言 一、系统设计 二、代码实现 1. 环境准备 2. 安装依赖 3. 创建停车场组件 4. 集…

时序预测 | Matlab实现BiTCN-BiLSTM双向时间卷积神经网络结合双向长短期记忆神经网络时间序列预测

时序预测 | Matlab实现BiTCN-BiLSTM双向时间卷积神经网络结合双向长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab实现BiTCN-BiLSTM双向时间卷积神经网络结合双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现BiTCN…

本地主机连接Linux虚拟机中的mongodb,并使用studio 3T连接,同时项目启动连接mongodb刷新数据库

本部分只做个人纪录 ** 1.安装mongodb ** 本部分为尚硅谷的电影推荐系统的文档&#xff0c;具体以实际存放位置为准 // 通过WGET下载Linux版本的MongoDB [bigdatalinux ~]$ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.4.3.tgz// 将压缩包解压到…

Python 深度学习第二版(GPT 重译)(二)

四、入门神经网络&#xff1a;分类和回归 本章涵盖 您的第一个真实世界机器学习工作流示例 处理矢量数据上的分类问题 处理矢量数据上的连续回归问题 本章旨在帮助您开始使用神经网络解决实际问题。您将巩固从第二章和第三章中获得的知识&#xff0c;并将所学应用于三个新…

Java newInstance方法学习

用newInstance与用new是有区别的&#xff0c;区别在于创建对象的方式不一样&#xff0c;前者是使用类加载机制&#xff1b; newInstance方法要求该 Class 对应类有无参构造方法&#xff1b; 执行 newInstance()方法实际上就是使用对应类的无参构造方法来创建该类的实例&#x…

Golang 异步(bsd/linux)io

Golang 异步(bsd/linux)io 在日常开发中&#xff0c;读写文件的底层调用函数是syscall.Read/Write。一切都是围绕这两个函数展开的&#xff0c;不过有时候需要或者就是单纯想异步执行。liburing是linux上一个很好的原生异步io库&#xff0c;这里需要适配bsd派系的系统&#xf…

Redis面试题以及答案

1. 什么是Redis&#xff1f;它主要用来什么的&#xff1f; Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并…

00. 认识 Java 语言与安装教程

认识 Java Java 在 20 多年发展过程中&#xff0c;与时俱进&#xff0c;为了适应时代的需要&#xff0c;经历过两次重大的版本升级&#xff0c;一个是 Java 5&#xff0c;它提供了泛型等重要的功能。另一个是提供了 Lambda 表达式等重要的功能的 Java 8。 一些重要的 Java 的…

GitHub配置SSH Key(详细版本)

GitHub配置SSH Key的目的是为了帮助我们在通过git提交代码是&#xff0c;不需要繁琐的验证过程&#xff0c;简化操作流程。比如新建的仓库可以下载, 但是提交需要账号密码。 步骤 一、设置git的user name和email 如果你是第一次使用&#xff0c;或者还没有配置过的话需要操作…

linux内核input子系统概述

目录 一、input子系统二、关键数据结构和api2.1 数据结构2.1.1 input_dev2.1.2 input_handler2.1.3 input_event2.1.4 input_handle 2.2 api接口2.2.1 input_device 相关接口input_device 注册流程事件上报 2.2.2 input handle 相关接口注册 handle指定 handle 2.2.3 input han…

Python 深度学习第二版(GPT 重译)(一)

前言 序言 如果你拿起这本书&#xff0c;你可能已经意识到深度学习在最近对人工智能领域所代表的非凡进步。我们从几乎无法使用的计算机视觉和自然语言处理发展到了在你每天使用的产品中大规模部署的高性能系统。这一突然进步的后果几乎影响到了每一个行业。我们已经将深度学…

Java与Go:指针

在计算机内存中&#xff0c;每个变量都有一个唯一的地址&#xff0c;指针就是用来保存这个地址的变量。通过指针&#xff0c;我们可以间接地访问和修改存储在该地址处的数据。今天我们来聊一聊Java和Go指针&#xff0c;预告一下&#xff0c;我们需要借助C语言做一些小小的比较。…