UniRx 基础用法
一. Unity 响应式编程:UniRx
1. 前言
先前开发游戏过程中,有用到Zenject + UniTask + UniRx + Localization + GoogleSheet + Addressable的方式来开发游戏,后续会逐个介绍,本篇进行UniRx的介绍,希望起到抛砖引玉作用。
在Unity开发中,响应式编程(Reactive Programming)是一种非常强大的编程范式,它可以帮助我们更好地处理异步事件流。UniRx 是一个专为Unity设计的响应式编程库,它基于Reactive Extensions (Rx) 实现,提供了丰富的功能和灵活的API。本文将详细介绍UniRx的使用方法和注意事项。
2.UniRx 的好处和坏处
1.好处
- 简洁的事件处理:UniRx 提供了简洁的API,可以轻松处理复杂的事件流。
- 强大的组合操作:UniRx 提供了丰富的操作符,可以对事件流进行过滤、转换、组合等操作。
- 与Unity无缝集成:UniRx 支持Unity的各种事件,如Update、FixedUpdate、OnDestroy等。
- 提高代码可读性:响应式编程可以使代码更加简洁和易读,减少回调地狱和状态管理的复杂性。
2.坏处
- 学习曲线陡峭:响应式编程的概念和API较为复杂,需要一定的学习成本。
- 性能开销:在高频率事件处理和大量数据流操作时,可能会带来一定的性能开销。
- 调试困难:由于事件流的异步和组合特性,调试和定位问题可能会比较困难。
3.UniRx 与 LINQ 的区别
- LINQ:主要用于对集合(如数组、列表等)进行查询和操作,操作的是静态数据。
- UniRx:主要用于处理异步事件流,操作的是动态数据。
// LINQ 示例
int numbers = {
1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
Debug.Log("Even numbers: " + string.Join(", ", evenNumbers));
// UniRx 示例
Observable.Range(1, 5)
.Where(n => n % 2 == 0)
.Subscribe(n => Debug.Log("Even number: " + n));
在上面的示例中,LINQ 用于对数组进行查询,而 UniRx 用于处理动态的事件流。
4.UniRx 的基本用法
1.创建Observable
UniRx 提供了多种方式来创建Observable对象。
// 创建一个简单的Observable
var observable = Observable.Return(42);
observable.Subscribe(value => Debug.Log("Received value: " + value));
// 创建一个定时Observable
var timer = Observable.Timer(System.TimeSpan.FromSeconds(1));
timer.Subscribe(_ => Debug.Log("Timer triggered"));
// 创建一个范围Observable
var range = Observable.Range(1, 5);
range.Subscribe(value => Debug.Log("Range value: " + value));
2.操作符
UniRx 提供了丰富的操作符来对Observable进行操作。
Observable.Range(1, 10)
.Where(n => n % 2 == 0)
.Select(n => n * n)
.Subscribe(n => Debug.Log("Processed value: " + n));
在上面的示例中,我们使用 Where 操作符过滤偶数,并使用 Select 操作符对值进行平方处理。
3.订阅
订阅是响应式编程的核心,通过订阅来处理Observable的事件。
Observable.EveryUpdate()
.Subscribe(_ => Debug.Log("Update event"))
.AddTo(this); // 自动取消订阅
上面的示例中,我们订阅了 EveryUpdate 事件,并在 MonoBehaviour 销毁时自动取消订阅。
4.网络操作
UniRx 可以与Unity的网络操作结合使用,处理异步的网络请求。
private void Start()
{
FetchData("https://jsonplaceholder.typicode.com/todos/1")
.Subscribe(response => Debug.Log("Response: " + response));
}
private IObservable<string> FetchData(string url)
{
return Observable.FromCoroutine<string>(observer => FetchDataCoroutine(url, observer));
}
private IEnumerator FetchDataCoroutine(string url, IObserver<string> observer)
{
using (UnityWebRequest request = UnityWebRequest.Get(url))
{
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
observer.OnNext(request.downloadHandler.text);
observer.OnCompleted();
}
else
{
observer.OnError(new System.Exception(request.error));
}
}
}
我们使用 Observable.FromCoroutine 方法将 UnityWebRequest 的协程转换为Observable对象。
5.并行操作
UniRx 提供了多种方式来处理并行操作,可以使用 Merge、Zip 等操作符。
var observable1 = Observable.Timer(System.TimeSpan.FromSeconds(1)).Select(_ => "Task 1 completed");
var observable2 = Observable.Timer(System.TimeSpan.FromSeconds(2)).Select(_ => "Task 2 completed");
Observable.Merge(observable1, observable2)
.Subscribe(message => Debug.Log(message));
我们使用 Merge 操作符将两个Observable合并,并行执行它们。
6.UI 操作
UniRx 可以与Unity的UI系统结合使用,处理UI事件。
1.按钮点击事件
myButton.OnClickAsObservable()
.Subscribe(_ => myText.text = "Button clicked!");
2.动条事件
mySlider.OnValueChangedAsObservable()
.Subscribe(value => myText.text = "Slider value: " + value);
3.Toggle 切换事件
myToggle.OnValueChangedAsObservable()
.Subscribe(isOn => myText.text = "Toggle is " + (isOn ? "On" : "Off"));
4.输入框事件
myInputField.OnValueChangedAsObservable()
.Subscribe(text => myText.text = "Input: " + text);
5.注意事项
- 内存泄漏:在使用UniRx时,需要注意订阅的取消,以避免内存泄漏。可以使用 AddTo 方法将订阅与 MonoBehaviour 的生命周期绑定。
- 性能开销:在高频率事件处理和大量数据流操作时,可能会带来一定的性能开销,需要注意优化。
- 调试困难:由于事件流的异步和组合特性,调试和定位问题可能会比较困难。可以使用 Do 操作符来调试事件流。
Observable.Range(1, 5)
.Do(value => Debug.Log("Before filter: " + value))
.Where(value => value % 2 == 0)
.Do(value => Debug.Log("After filter: " + value))
.Subscribe(value => Debug.Log("Final value: " + value));
6.总结
UniRx 是一个非常强大的响应式编程库,可以帮助我们更高效地处理异步事件流,提高代码的可读性和可维护性。希望大家在实际项目中能够灵活应用UniRx,提高代码质量。