Что такое реактивное программирование и причём здесь R3?Реактивное программирование - это парадигма, в котором вместо того, чтобы напрямую связывать объекты, мы реагируем на изменения и уведомляем объекты о них. Объекты в свою очередь сами решают, что с этой информацией делать. Уведомление происходит с помощью потока событий. Сам поток событий представляет из себя некоторый менеджер событий, на который можно подписаться и отписаться.
Пример. У нас есть здоровье персонажа. Где оно хранится? Например, у Player, который при нанесении урона его дёргает и меняет его стейт.
В реактивном программировании, вместо того, чтобы хранить здоровье и дёргать его каждый раз, мы отправляем уведомление в поток событий, который уже в свою очередь уведомляет здоровье о том, что ему был нанесён урон. Таким образом, теперь Player и Health не связаны напрямую. Изменения происходят через поток событий.
Если вы посмотрите на свой ООПшный код, то вы возможно увидите, что множество действий у вас происходит в рантайме и на них необходима реакция. У вас есть объект, который получает ссылку на другой объект для подписки. Этого можно избежать, передавая потоки событий вместо самих объектов. Таким образом, мы отказываемся от прямого контроля в пользу обмена данных через потоки событий.
R3 - это фреймворк для юнити, который был создан для упрощения внедрения реактивного программирования в ваш код.
Для это используются:
ReactiveProperty<T> - это свойство, за которым непосредственно ведётся наблюдение. Если его значение изменилось - об этом отправляется уведомление.
var health = new ReactiveProperty<int>(100);
health.Subscribe(value => Debug.Log($"HP: {value}")); // логика, которая будет происходить при изменении
health.Value -= 10; // автоматически уведомит подписчиков
Observable<T> - это абстракция потока событий.
Он сам по себе ничего не хранит, а лишь сообщает: «произошло событие - вот данные».
На Observable можно подписаться, применить к нему операторы (фильтрация, трансформация, объединение потоков) и в итоге получить реакцию в нужном месте кода.
Простой пример:
Observable.EveryUpdate()
.Where(_ => Input.GetKeyDown(KeyCode.Space))
.Subscribe(_ => Jump());
Здесь:
• EveryUpdate() - поток событий, который срабатывает каждый кадр
• Where - фильтрация
• Subscribe - реакция
Никаких Update, флагов и проверок по всему коду.
Чем R3 отличается от простых ивентов?На первый взгляд кажется что реактивность не отличается от ивентов, просто другой синтаксис.
Но ключевое отличие - композиция и управление жизненным циклом.
С реактивщиной вы можете легко:
• объединять потоки
• трансформировать данные
• ограничивать частоту событий
• отменять цепочки событий
Например обработать событие когда hp стало 0, но только один раз:
health
.Where(hp => hp <= 0)
.Take(1)
.Subscribe(_ => Die());
Без R3 такой код будет намного длиннее и грязнее.
Контроль жизненного цикла - R3 умеет автоматически отписываться от событий вместе с GameObject:
health
.Subscribe(_ => HandleHalthChange())
.AddTo(this);
GameObject уничтожился - подписка тоже. Без утечек и забытых отписок.
Когда R3 реально удобен?• UI (биндинги, состояния, реакция на данные)
• игровые состояния (HP, статы, бафы, дебафы)
• ввод (Input, тайминги, кулдауны)
• асинхронщину и цепочки событий
То есть там, где:
• много изменений во времени
• много зависимых реакций
• хочется меньше связности
ИтогR3 - это инструмент, который:
• снижает связность
• упрощает работу с событиями
• делает код декларативным
• заставляет думать потоками, а не состояниями
Если сказать просто - то это как LINQ в C#. Вы можете фильтровать коллекции самостоятельно, написав огромные циклы for. Но вам предоставлен инструмент с готовыми методами, который позволяет писать более декларативно, писать "что именно нужно сделать", а не "как сделать то, что нужно".
Ссылка на репозиторий R3 с документацией и инструкцией по установке🚀
Пост Guru Unity: @Minerope