Как сделать так чтобы UniTask вела себя как Coroutine?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
UniTask как альтернатива Coroutine: подходы и практика
UniTask — это современная библиотека для асинхронного программирования в Unity, предлагающая более эффективную и гибкую альтернативу традиционным Coroutine. Хотя они имеют разные реализации, можно добиться схожего поведения, используя несколько ключевых подходов.
Основные сходства и различия
UniTask работает на основе async/await паттерна C#, тогда как Coroutine использует итераторы (IEnumerator). UniTask обеспечивает нулевые аллокации в runtime, лучшую производительность и интеграцию с CancellationToken для управления жизненным циклом.
Эмуляция поведения Coroutine
Чтобы UniTask вела себя подобно Coroutine, нужно имитировать два основных аспекта: плавное выполнение по кадрам и возможность остановки.
1. Использование UniTask.Delay для поэтапного выполнения
Coroutine часто используют yield return new WaitForSeconds() для задержек. В UniTask для этого используется UniTask.Delay, который можно ожидать с await.
using Cysharp.Threading.Tasks;
using UnityEngine;
public class UniTaskExample : MonoBehaviour
{
async UniTaskVoid Start()
{
// Эмулируем Coroutine, которая выполняется 5 секунд с шагом в 1 секунду
for (int i = 0; i < 5; i++)
{
Debug.Log($"Шаг: {i}");
await UniTask.Delay(1000); // Задержка в миллисекундах
}
Debug.Log("Завершено");
}
}
Для более точной эмуляции WaitForSeconds используйте UniTask.Delay(TimeSpan.FromSeconds(1)) или конфигурируйте задержку с помощью ignoreTimeScale.
2. Ожидание конца кадра и другие Yield Instructions
Coroutine использует yield return null или yield return new WaitForEndOfFrame(). В UniTask для этого есть специальные методы:
async UniTaskVoid UpdateLikeCoroutine()
{
while (true)
{
// Эквивалент yield return null в Update
await UniTask.Yield();
// Ваша логика здесь
transform.Translate(Vector3.forward * Time.deltaTime);
}
}
Для других сценариев:
- WaitForEndOfFrame:
await UniTask.WaitForEndOfFrame(this.GetCancellationTokenOnDestroy()); - WaitForFixedUpdate:
await UniTask.WaitForFixedUpdate(); - WaitUntil/WaitWhile: используйте
await UniTask.WaitUntil(() => condition)или комбинируйте с циклом.
3. Управление жизненным циклом через CancellationToken
Одно из слабых мест Coroutine — сложность принудительной остановки. В UniTask это решается через CancellationToken.
using Cysharp.Threading.Tasks;
using System.Threading;
using UnityEngine;
public class StoppableTask : MonoBehaviour
{
private CancellationTokenSource _cancellationTokenSource;
void Start()
{
_cancellationTokenSource = new CancellationTokenSource();
RunTask().Forget();
}
async UniTaskVoid RunTask()
{
try
{
while (true)
{
await UniTask.Delay(1000, cancellationToken: _cancellationTokenSource.Token);
Debug.Log("Tick");
}
}
catch (OperationCanceledException)
{
Debug.Log("Задача отменена");
}
}
void OnDestroy()
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
}
}
4. Запуск и остановка как у Coroutine
Для запуска UniTask из MonoBehaviour просто вызовите асинхронный метод с суффиксом Forget() или используйте UniTask.Void. Для остановки — отменяйте токен.
// Запуск
private void Start()
{
MyCoroutineLikeTask().Forget();
}
// Остановка через токен
private CancellationTokenSource _cts;
public void StopMyTask()
{
_cts?.Cancel();
}
Практические рекомендации
- Для новых проектов предпочтительно использовать UniTask из-за производительности и лучшего контроля.
- При миграции с Coroutine заменяйте
yield returnна соответствующиеawait UniTask.*методы. - Используйте
UniTask.RunOnThreadPoolдля CPU-нагрузочных операций, что невозможно в Coroutine. - Избегайте смешивания Coroutine и UniTask в одной цепочке операций без необходимости.
UniTask не просто эмулирует Coroutine, а предлагает более мощный инструментарий для асинхронных операций в Unity, сохраняя при этом привычные паттерны работы.