← Назад к вопросам

Что такое UniTask и UniRx? Для чего они используются?

1.0 Junior🔥 111 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

UniTask и UniRx: Асинхронность и реактивный подход в Unity

UniTask и UniRx — это два популярных сторонних библиотеки для Unity, которые решают фундаментальные проблемы стандартного C#/Unity подхода: асинхронное программирование и реактивное программирование. Они существенно улучшают читаемость, производительность и управление состоянием в проектах.

UniTask: Современная асинхронность без накладных расходов

UniTask — это библиотека, созданная Cysharp (известный также по ZString, MessagePack), которая предлагает альтернативу стандартным Task из C# и Unity Coroutine.

Для чего используется UniTask:

  • Замена Coroutine: Coroutines в Unity имеют ограничения (нельзя возвращать значения, сложная обработка ошибок, зависимость от MonoBehaviour). UniTask предоставляет легковесные "асинхронные методы", которые могут выполняться без привязки к игровому объекту.
  • Оптимизация производительности: Task из .NET генерирует значительные аллокации памяти (каждый Task — это класс). UniTask использует custom value-type задачи, что минимизирует аллокации и нагрузку на GC, что критично для игр с 60 FPS.
  • Упрощение асинхронных операций: Отличная интеграция с Unity's AsyncOperation (например, Resources.LoadAsync, SceneManager.LoadSceneAsync) и жизненным циклом (MonoBehaviour's события Start, Update можно легко превратить в асинхронные потоки).

Ключевые особенности и пример:

UniTask позволяет писать код, который выглядит как синхронный, но работает асинхронно и эффективно.

using Cysharp.Threading.Tasks;
using UnityEngine;

public class UniTaskExample : MonoBehaviour
{
    async UniTaskVoid Start()
    {
        // Асинхронная загрузка ресурса без аллокаций Task
        Texture2D texture = await Resources.LoadAsync<Texture2D>("MyTexture").ToUniTask();

        // Асинхронная задержка, которая не создает GameObject как Coroutine
        await UniTask.Delay(1000); // Миллисекунды

        // Ожидание нескольких асинхронных операций параллельно
        var (sceneLoad, assetLoad) = await UniTask.WhenAll(
            SceneManager.LoadSceneAsync("Level2").ToUniTask(),
            Addressables.LoadAssetAsync<GameObject>("Prefab").ToUniTask()
        );

        Debug.Log("Все операции завершены!");
    }
}

Главное преимущество здесь — async UniTaskVoid метод в Start(). Он запускается автоматически, не требует yield return, и все операции внутри него выполняются без накладных расходов стандартных Task.

UniRx: Реактивное программирование для управления событиями и состоянием

UniRx (Unity Reactive Extensions) — это порт Reactive Extensions (Rx) для .NET в экосистему Unity. Он реализует парадигму реактивного программирования, где данные рассматриваются как потоки (streams), а изменение состояния — как события в этих потоках.

Для чего используется UniRx:

  • Управление сложными событиями: Объединение, фильтрация, трансформация событий из разных источников (ввод пользователя, изменения данных, сообщения между системами) в единые, легко читаемые потоки.
  • Реактивные привязки данных (Data Binding): Создание автоматических связей между свойствами модели (например, здоровья игрока) и UI элементами (полоска здоровья). При изменении свойства UI мгновенно обновляется.
  • Замена стандартных событий Unity: UnityEvent и делегаты C# часто приводят к разрозненному, сложному в управлении коду. UniRx предоставляет централизованный способ наблюдения за изменениями.

Ключевые особенности и пример:

В UniRx основная абстрактная единица — IObservable<T> (поток данных) и IObserver<T> (подписчик на поток).

using UniRx;
using UniRx.Triggers; // Для интеграции с Unity событиями
using UnityEngine;
using UnityEngine.UI;

public class UniRxExample : MonoBehaviour
{
    public Button myButton;
    public Slider healthSlider;
    private ReactiveProperty<int> currentHealth = new ReactiveProperty<int>(100);

    void Start()
    {
        // 1. Реактивная обработка кликов по кнопке с фильтрацией и трансформацией потока.
        myButton.OnClickAsObservable() // Поток событий клика
            .Where(_ => currentHealth.Value > 0) // Фильтрация: только если здоровье > 0
            .ThrottleFirst(TimeSpan.FromSeconds(1)) // Гашение: не чаще 1 раза в секунду
            .Subscribe(_ => currentHealth.Value -= 10) // Подписка: при событии уменьшаем здоровье
            .AddTo(this); // Автоматическая отписка при уничтожении GameObject

        // 2. Реактивная привязка данных: ползунок автоматически обновляется при изменении здоровья.
        currentHealth
            .Select(health => (float)health / 100f) // Трансформация: целое число -> коэффициент 0..1
            .SubscribeToSlider(healthSlider) // Специальный метод для привязки к Slider
            .AddTo(this);

        // 3. Создание сложного потока из нескольких источников.
        Observable.EveryUpdate() // Поток: каждый кадр
            .Where(_ => Input.GetKeyDown(KeyCode.Space))
            .Merge(myButton.OnClickAsObservable()) // Объединение с потоком кликов
            .Subscribe(_ => Debug.Log("Активация от Space или клика!"))
            .AddTo(this);
    }
}

Сравнение и совместное использование

  • UniTask фокусируется на асинхронных операциях и производительности, заменяя корутины и Task.
  • UniRx фокусируется на потоках событий и реактивных связях, заменяя традиционные системы событий и ручное управление состоянием.

Они могут мощно комбинироваться. Например, UniRx Observable можно конвертировать в UniTask для ожидания конкретного события, или использовать UniTask для асинхронной загрузки данных, которые затем подаются в реактивный поток UniRx для обновления UI.

В современных проектах Unity эти библиотеки стали почти стандартом для чистого, поддерживаемого и высокопроизводительного кода, особенно в сложных клиент-серверных или UI-насыщенных приложениях.