文章目录
- 前言
- 一、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#的Task
和async/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.WaitUntil
和 UniTask.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 来处理异步操作中的错误,确保程序的稳定性。