Unity 的 UniTask 是一种用于异步编程的 C# 库,它扩展了 .NET 中的 Task 和 await/async 模式。
1、异步等待:使用 await 关键字,可以等待一个异步操作完成,这样就不需要手动处理回调或使用协程。
2、延迟执行:使用 UniTask.Delay 方法可以在指定的时间后执行一个操作,而不需要使用协程或计时器。
3、同步化操作:使用 UniTask.SwitchToMainThread 方法可以在主线程上执行一个操作,这对于访问 Unity 的组件或 API 非常有用。
4、迭代器:使用 UniTask.ToCoroutine 方法可以将一个 UniTask 对象转换为 IEnumerator,从而可以在协程中使用。
5、任务链:使用 UniTask.WhenAll 或 UniTask.WhenAny 方法可以创建一个任务链,等待多个异步操作完成后执行下一步操作。
6、取消任务:使用 UniTaskCancellationToken 类可以取消异步操作,这对于长时间运行的操作非常有用
using Cysharp.Threading.Tasks; // 首先需要引入UniTask库
public class UniTaskExample : MonoBehaviour
{
async void Start()
{
// 使用UniTask.Delay进行延迟操作
await UniTask.Delay(TimeSpan.FromSeconds(1.0f));
Debug.Log("Waited for 1 second.");
// 异步加载资源
var www = UnityWebRequest.Get("https://example.com/somefile.txt");
using (var op = UnityWebRequestAsyncOperation.FromAsync(www.SendWebRequest))
{
await op.ToUniTask(); // 将UnityWebRequest的异步操作转换为UniTask
if (www.result == UnityWebRequest.Result.Success)
{
string text = www.downloadHandler.text;
Debug.Log("Downloaded content: " + text);
}
else
{
Debug.LogError($"Failed to download: {www.error}");
}
}
// 异步更新UI
await UniTask.SwitchToMainThread(); // 切换到主线程更新UI
SomeUIComponent.text = "Updated via UniTask";
}
}
在上述示例中:
我们首先展示了如何使用 UniTask.Delay
来实现延迟操作,与 yield return new WaitForSeconds
相比,这种方式不会阻塞主运行线程。
然后我们演示了如何结合Unity的 UnityWebRequest
异步下载资源,并将其转换为 UniTask
,以便使用 await
关键字来等待请求完成。
最后展示了如何通过 UniTask.SwitchToMainThread
方法将异步任务的结果应用到主线程上的UI元素上,确保UI更新是安全的。
UniTask还可以用于很多其他场景,比如I/O操作、动画等待、游戏逻辑异步化等,能够极大地简化和优化Unity中的异步编程模型。
当然,UniTask 的功能远不止这些基础使用案例。这里再提供一些进阶用法示例:
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
public class UniTaskExampleAdvanced : MonoBehaviour
{
async void Start()
{
var tasks = new List<UniTask<string>>(); // 创建一个任务列表来存储异步操作
// 并发启动多个下载任务
for (int i = 0; i < 5; i++)
{
string url = $"https://example.com/resource{i}.txt";
tasks.Add(DownloadTextAsync(url));
}
// 使用UniTask.WhenAll等待所有任务完成
var results = await UniTask.WhenAll(tasks);
foreach (var result in results)
{
Debug.Log($"Downloaded text: {result}");
}
}
private async UniTask<string> DownloadTextAsync(string url)
{
var www = UnityWebRequest.Get(url);
using (var op = UnityWebRequestAsyncOperation.FromAsync(www.SendWebRequest))
{
await op.ToUniTask();
if (www.result == UnityWebRequest.Result.Success)
{
return www.downloadHandler.text;
}
else
{
Debug.LogError($"Failed to download: {www.error}");
return null;
}
}
}
}
在这个例子中,我们创建了多个 DownloadTextAsync
异步任务,并使用 UniTask.WhenAll
来等待所有任务都完成。当所有任务完成后,我们可以获取到每个任务的结果。
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class UniTaskCancellationExample : MonoBehaviour
{
public Button startButton;
public Button cancelButton;
private CancellationTokenSource cancellationTokenSource;
private async void Start()
{
startButton.onClick.AddListener(async () => await LongRunningTaskAsync());
cancelButton.onClick.AddListener(() => CancelLongRunningTask());
cancellationTokenSource = new CancellationTokenSource();
}
private async UniTaskVoid LongRunningTaskAsync()
{
try
{
Debug.Log("Starting long running task...");
await UniTask.Delay(TimeSpan.FromSeconds(10), cancellationTokenSource.Token);
Debug.Log("Long running task completed.");
}
catch (OperationCanceledException)
{
Debug.LogWarning("Long running task was cancelled.");
}
}
private void CancelLongRunningTask()
{
cancellationTokenSource.Cancel();
}
}
这个示例展示了如何通过传递 CancellationToken
来支持任务的取消。当用户点击“取消”按钮时,我们会调用 cancellationTokenSource.Cancel()
来取消正在运行的长时间任务,如果任务监听到了取消信号,则会抛出 OperationCanceledException
异常,从而可以处理取消逻辑。
在Unity中,我们有时需要在Update函数中执行异步操作,并在下一次Update时继续处理。UniTask可以方便地实现这一需求:
using Cysharp.Threading.Tasks;
using UnityEngine;
public class UniTaskWithUpdate : MonoBehaviour
{
private async UniTaskVoid AsyncProcess()
{
for (int i = 0; i < 10; i++)
{
Debug.Log($"Processing step {i}...");
await UniTask.Yield(); // 暂停当前任务,等待下一个Update周期
}
Debug.Log("Async process completed.");
}
private UniTask runningTask;
void Update()
{
if (runningTask.IsCompleted)
{
// 如果上一轮的异步任务已完成,则开始新的任务
runningTask = AsyncProcess();
runningTask.ToCoroutine(this); // 将UniTask转换为IEnumerator以配合StartCoroutine
}
}
}
在这个示例中,我们在Update函数中启动并跟踪一个异步任务。通过UniTask.Yield()
,我们可以在每个异步步骤之间暂停,等待下一次Update调用。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using Cysharp.Threading.Tasks;
public class UniTaskAssetLoadingExample : MonoBehaviour
{
public List<GameObject> prefabsToLoad;
async void Start()
{
var tasks = new List<UniTask<GameObject>>();
foreach (var prefabPath in prefabsToLoad.Select(p => p.name))
{
tasks.Add(LoadPrefabAsync(prefabPath));
}
var loadedPrefabs = await UniTask.WhenAll(tasks);
foreach (var loadedPrefab in loadedPrefabs)
{
Instantiate(loadedPrefab, transform);
}
}
private async UniTask<GameObject> LoadPrefabAsync(string prefabPath)
{
var handle = Addressables.LoadAssetAsync<GameObject>(prefabPath);
return await handle.Task.AsUniTask(cancellationToken: default);
}
}
这个例子展示了如何利用UniTask来并发加载多个资源,并在所有资源加载完毕后一次性进行处理。这里我们使用了Addressables来进行资源加载,通过UniTask.WhenAll
等待所有加载任务完成。
源码地址:https://github.com/Cysharp/UniTask
python推荐学习汇总连接:
50个开发必备的Python经典脚本(1-10)
50个开发必备的Python经典脚本(11-20)
50个开发必备的Python经典脚本(21-30)
50个开发必备的Python经典脚本(31-40)
50个开发必备的Python经典脚本(41-50)
————————————————
最后我们放松一下眼睛