UniRx学习笔记
定时器
1
2
3
4
5
6
7
8
9
10
11
12public class UniRxTimer : MonoBehaviour { private void Start() { Observable.Timer(TimeSpan.FromSeconds(5.0f)) .Subscribe(_ => { Debug.Log("Do Sth..."); }); } }
独立的Update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45public class UpdateExample : MonoBehaviour { enum ButtonState { None, Clicked, DoubleClicked } private void Start() { ButtonState buttonState = ButtonState.None; bool buttonClicked = false; //监听鼠标左键 Observable.EveryUpdate() .Subscribe(_ => { if (Input.GetMouseButtonDown(0)) { print("left mouse button clicked"); buttonClicked = true; } }); //监听鼠标右键 Observable.EveryUpdate() .Subscribe(_ => { if (Input.GetMouseButtonDown(1)) { print("right mouse button clicked"); buttonClicked = true; } }); Observable.EveryUpdate() .Subscribe(_ => { //监听状态 if (buttonClicked && buttonState == ButtonState.None) { buttonState = ButtonState.Clicked; } print(buttonState); }); } }
这种应用比较初级,随着学习的深入,会有更好的使用方法。
Observable.XXX().Subscribe() 是⾮常典型的 UniRx 格式。
只要理解什么意思就可以看懂⼤部分的 UniRx 的⽤法了。
⾸先解决词汇问题:
Observable: 可观察的,形容词,形容后边的词(Timer) 是可观察的,我们可以粗暴地把 Observable 后边的理解成发布者。
Timer: 定时器,名词,被 Observable 描述,所以是发布者,是事件的发送⽅。
Subscribe: 订阅,动词,订阅谁呢?当然是前边的 Timer,这⾥可以理解成订阅者,也就是事件的接
收⽅。
AddTo: 暂不⽤理解。
连起来则是:可被观察(监听)的.Timer().订阅()
顺下来应该是:订阅可被观察的定时器。
其概念关系很容易理解。
• Timer 是可观察的。
• 可观察的才能被订阅。
Observable.XXX().Subscribe();
可被观察(监听)的 XX,注册。
以上笔者从发布者和订阅者这个⻆度来进⾏的介绍,以便⼤家理解。
但是 UniRx 的侧重点,不是发布者和订阅者这两个概念如何使⽤,⽽是事件从发布者到订阅者之间的
过程如何处理。
所以两个点不重要,重要的是两点之间的线,也就是事件的传递过程。
1
2
3
4
5
6
7
8
9
10
11
12
13public class IntroExample : MonoBehaviour { private void Start() { Observable.EveryUpdate()//开启Update的事件监听 .Where(_ => Input.GetMouseButtonDown(0))//进行一个鼠标是否抬起的判断 .First()//只获取第一次的点击事件 .Subscribe(_=> {//订阅/处理事件 print("MouseButton Down."); }); } }
where操作符
-
EveryUpdate 是事件的发布者。他会每帧会发送⼀个事件过来。
-
Subscribe 是事件的接收者,接收的是 EveryUpdate 发送的事件。
-
Where 则是在事件的发布者和接收者之间的⼀个过滤操作。会过滤掉不满⾜条件的事件。
First操作符
获取第一个通过的事件,First还可以传一个条件,所以上述代码可以不使用where,精简为:
复制代码1
2
3
4
5
6
7
8
9
10
11
12
13public class IntroExample : MonoBehaviour { private void Start() { Observable.EveryUpdate()//开启Update的事件监听 .First(_ => Input.GetMouseButtonDown(0))//只获取第一次的点击事件 .Subscribe(_=> {//订阅/处理事件 print("MouseButton Down."); }) .AddTo(this); } }
UGUI的支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51/**************************************************** 文件:UIExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/9 17:8:53 功能:UniRx对UGUI的支持 *****************************************************/ using UnityEngine; using System; using System.Collections; using UniRx; using UniRx.Triggers; using UnityEngine.UI; namespace UniRxLesson { public class UIExample : MonoBehaviour { private void Start() { var button = transform.Find("Button").GetComponent<Button>(); button.OnClickAsObservable() .Subscribe(_ => { print("button clicked"); }); var toggle = transform.Find("Toggle").GetComponent<Toggle>(); toggle.OnValueChangedAsObservable() .Subscribe(on => { print(on); }); var image = transform.Find("Image").GetComponent<Image>(); image.OnBeginDragAsObservable() .Subscribe(_ => { print("Begin Drag"); }); image.OnDragAsObservable() .Subscribe(_ => { print("On Dragging"); }); image.OnEndDragAsObservable() .Subscribe(_ => { print("End Drag"); }); } } }
使用UniRx可以非常方便的对UGUI进行事件发布和订阅,免去实现一些接口的工作。除此之外,UniRx还可以用在任何UNITY Event中。
响应式属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39/**************************************************** 文件:ReactivePropertyExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 10:31:39 功能:UniRx的响应式属性示例 *****************************************************/ using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using UniRx; namespace UniRxLesson { public class ReactivePropertyExample : MonoBehaviour { public ReactiveProperty<int> Age = new ReactiveProperty<int>(0); private void Start() { Age.Subscribe(age => { print("inner recevied Age change"+age ); }); Age.Value = 10;//不管什么时候值发生改变,订阅改变事件的函数必定响应 } } public class PersonView { ReactivePropertyExample mReactivePropertyExample = null; void Init() { mReactivePropertyExample.Age.Subscribe((age) => { Debug.Log(age); }); } } }
MVP(Model-View-Presenter)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53/**************************************************** 文件:EnemyExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 11:29:27 功能:MVP简单示例 *****************************************************/ using UnityEngine; using UnityEngine.UI; using System; using System.Collections; using System.Collections.Generic; using UniRx; namespace UniRxLesson { public class EnemyExample : MonoBehaviour { EnemyModel enemy = new EnemyModel(200); private void Start() { var btn_Attack = transform.Find("Button").GetComponent<Button>(); var txt_HPText = transform.Find("Text").GetComponent<Text>(); btn_Attack.OnClickAsObservable() .Subscribe(_ => { enemy.HP.Value -= 99; }); enemy.HP.SubscribeToText(txt_HPText); /* enemy.isDead .Where(isDead => isDead) .Subscribe(_ => { btn_Attack.interactable = false; });*/ enemy.isDead //.Where(isDead => isDead) .Select(isDead => !isDead) .SubscribeToInteractable(btn_Attack); //两种方法都行,其中Select()函数是个转换,转换的结果直接赋值到subscribeToInteractable中 } } public class EnemyModel { public LongReactiveProperty HP; public IReadOnlyReactiveProperty<bool> isDead; public EnemyModel(long initHP) { HP = new LongReactiveProperty(initHP); isDead = HP.Select(hp => hp <= 0).ToReactiveProperty(); } } }
在 Unity 中,我们把 Scene 中的 GameObject 当做视图层,这些是在 Unity 的 Hierarchy 中定义的。
展示/控制层在 Unity 初始化时将视图层绑定。
SubscribeToText and SubscribeToInteractable 都是简洁的类似绑定的辅助函数。虽然这些⼯工具很简 单,但是⾮非常实⽤用。
在 Unity 中使⽤用开发体验⾮非常平滑,性能也很好,重要的是让你的代码更更简洁。
View -> ReactiveProperty -> Model -> RectiveProperty - View 完全⽤用响应式的⽅方式连接。UniRx 提供 了了所有的适配⽅方法和类,不不过其他的 MVVM (or MV*) 框架也可以使⽤用。UniRx/ReactiveProperty 只是 一个简单的⼯工具包。
Merge操作符
merge操作符用于合并多个事件流,相当于同时订阅两个事件流,只要任何一个事件流发生,订阅的函数和事件就会被执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/**************************************************** 文件:MergeExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 14:34:18 功能:Merge操作符的使用 *****************************************************/ using UnityEngine; using UniRx; using System; using System.Collections; using System.Collections.Generic; namespace UniRxLesson { public class MergeExample : MonoBehaviour { private void Start() { var leftClickedEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0)); var rightClickedEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(1)); Observable.Merge(leftClickedEvents, rightClickedEvents) .Subscribe(_ => { print("mouse Clicked"); }); } } }
用Merge和First做一个UI按钮事件锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38/**************************************************** 文件:PanelEventLockExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 14:48:52 功能:实现一个UI界面按钮锁的功能 *****************************************************/ using UnityEngine; using System; using UniRx; using UnityEngine.UI; namespace UniRxLesson { public class PanelEventLockExample : MonoBehaviour { private void Start() { var btn_A = transform.Find("ButtonA").GetComponent<Button>(); var btn_B = transform.Find("ButtonB").GetComponent<Button>(); var btn_C = transform.Find("ButtonC").GetComponent<Button>(); var aEvents = btn_A.OnClickAsObservable(); var bEvents = btn_B.OnClickAsObservable(); var cEvents = btn_C.OnClickAsObservable(); Observable.Merge(aEvents, bEvents, cEvents) .First() .Subscribe(_ => { print("Button clicked"); Observable.Timer(TimeSpan.FromSeconds(1.0f)) .Subscribe(__ => { gameObject.SetActive(false); }); }); } } }
Select的选择操作(操作后返回一个泛型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21private void Start() { var btn_A = transform.Find("ButtonA").GetComponent<Button>(); var btn_B = transform.Find("ButtonB").GetComponent<Button>(); var btn_C = transform.Find("ButtonC").GetComponent<Button>(); var aEvents = btn_A.OnClickAsObservable().Select(_ => "A"); var bEvents = btn_B.OnClickAsObservable().Select(_ => "B"); var cEvents = btn_C.OnClickAsObservable().Select(_ => "C"); Observable.Merge(aEvents, bEvents, cEvents) .First() .Subscribe(btnID =>//用btnID接收事件经过Select操作后的返回值 { print("Button clicked"+btnID); Observable.Timer(TimeSpan.FromSeconds(1.0f)) .Subscribe(__ => { gameObject.SetActive(false); }); }); }
第二章Unity和UniRx
UGUI的增强
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42/**************************************************** 文件:UGUIExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 15:39:12 功能:UniRx对unity UGUI的增强 *****************************************************/ using UnityEngine; using UniRx; using UnityEngine.UI; namespace UniRxLesson { public class UGUIExample : MonoBehaviour { [SerializeField] Button mButton; [SerializeField] Toggle mToggle; [SerializeField] Scrollbar mScrollbar; //[SerializeField] ScrollRect mScrollRect; [SerializeField] Slider mSlider; [SerializeField] InputField mInputField; [SerializeField] Text mText; void Start() { mButton.OnClickAsObservable().Subscribe(_ => Debug.Log("On Button Clicked")); mToggle.OnValueChangedAsObservable().Subscribe(on => Debug.Log("Toggle " + on)); mScrollbar.OnValueChangedAsObservable().Subscribe(scrollValue => Debug.Log("Scrolled " + scrollValue)); // mScrollRect.OnValueChangedAsObservable().Subscribe(scrollValue => //Debug.Log("Scrolled " + scrollValue)); mSlider.OnValueChangedAsObservable().Subscribe(sliderValue => Debug.Log("Slider Value " + sliderValue)); mInputField.OnValueChangedAsObservable().Subscribe(inputText => Debug.Log("Input Text: " + inputText)); //mInputField.OnEndEditAsObservable().Subscribe(result => //Debug.Log("Result :" + result)); //mInputField.OnEndEditAsObservable().SubscribeToText(mText); mInputField.OnValueChangedAsObservable().SubscribeToText(mText);//一个绑定操作,非常方便 } } }
Unity ⽣命周期 与 Trigger
对于 Unity 的 Observable 增强,我们在第⼀章就接触过了。
Observable.EveryUpdate() 就是⽀持的 Unity 的 API。
单单 Update 就是⽀持⾮常多细分类型的 Update 事件捕获。
1
2
3
4
5Observable.EveryFixedUpdate().Subscribe(_ => {}); Observable.EveryEndOfFrame().Subscribe(_ => {}); Observable.EveryLateUpdate().Subscribe(_ => {}); Observable.EveryAfterUpdate().Subscribe(_ => {});
除了 Update 还⽀持其他的事件,⽐如 ApplicationPause,Quit 等。
1
2
3
4Observable.EveryApplicationPause().Subscribe(paused => {}); Observable.EveryApplicationFocus().Subscribe(focused => {}); Observable.EveryApplicationQuit().Subscribe(_ => {}):
学习了以上这些,就不⽤再去创建⼀个单例类去实现⼀个诸如“应⽤程序退出事件监听”这种逻辑了。
命名⼏⾏代码就可以搞定的事情,何必再去创建⼀个类去搞定?
Trigger 简介
Observable.EveryUpdate() 这个 API 有的时候在某个脚本中实现,需要绑定 MonoBehaviour 的⽣命周 期(主要是 OnDestroy),当然也有的时候是全局的,⽽且永远不会被销毁的。
需要绑定 MonoBehaviour ⽣命周期的 EveryUpdate。只需要⼀个 AddTo 就可以进⾏绑定了。⾮常简
单,代码如下。
1
2
3
4Observable.EveryUpdate() .Subscribe(_ => {}) .AddTo(this);
但其实有更简洁的实现:
1
2
3this.UpdateAsObservable() .Subscribe(_ => {});
这种类型的 Observable 是什么呢?
答案是:Trigger,即触发器。
字如其意,很容易理解。
Trigger 类型的关键字
触发器,字如其意,是当某个事件发⽣时,则会将该事件发送到 Subscribe 函数中,⽽这个触发器,
本身是⼀个功能脚本,这个脚本挂在 GameObject 上,来监听 GameObject 的某个事件发⽣,事件发
⽣则会回调给注册它的 Subscribe 中。
触发器的操作和其他的事件源 (Observable) 是⼀样的,都⽀持 Where、First、Merge 等操作符。
Trigger 类型的 Observable 和我们之前讲的所有的 Observable 在表现上有⼀点不⼀样:
1. Trigger ⼤部分都是都是 XXXAsObsrevable 命名形式的。
2. 在使⽤ Trigger 的 GameObject 上都会挂上对应的 Observable XXXTrigger.cs 的脚本。
Trigger 在此之前我们是接触过的。
AddTo() 这个 API 其实是封装了⼀种 Trigger: ObservableDestroyTrigger。
2顾名思义,就是当 GameObject 销毁时获取事件的⼀个触发器。
⼀般的 Trigger 都会配合 MonoBehaviour ⼀起使⽤。
⽐如 ObservableDestroyTrigger 的使⽤代码如下:
1
2
3this.OnDestroyAsObservable() .Subscribe(_ => {});
除了 Destroy 还有⾮常多的 Trigger。
⽐如各种细分类型的 Update:
1
2
3
4this.FixedUpdateAsObservable().Subscribe(_ => {}); this.LateUpdateAsObservable().Subscribe(_ => {}); this.UpdateAsObservable().Subscribe(_ => {});
还有各种碰撞的 Trigger:
1
2
3
4
5
6
7
8this.OnCollisionEnterAsObservable(collision => {}); this.OnCollisionExitAsObservable(collision => {}); this.OnCollisionStayAsObservable(collision => {}); // 同样 2D 的也⽀持 this.OnCollision2DEnterAsObservable(collision2D => {}); this.OnCollision2DExitAsObservable(collision2D => {}); this.OnCollision2DStayAsObservable(collision2D => {});
⼀些脚本的参数监听:
1
2
3this.OnEnableAsObservable().Subscribe(_ => {}); this.OnDisableAsObservable().Subscribe(_ => {});
3.除了 MonoBehaviour ,Trigger 也⽀持了其他组件类型,⽐如 RectTransform、Transform、
UIBehaviour 等等。这⾥不再赘述。
详情可以查看 ObservableTriggerExtensions.cs 和 ObervableTriggerExtensions.Component.cs 中的 API。
UI Trigger
在项⽬中⽤的⽐较多的⼏个 Trigger:
1
2
3
4
5mImage.OnBeginDragAsObservable().Subscribe(dragEvent => {}); mGraphic.OnDragAsObservable().Subscribe(dragEvent => {}); mText.OnEndDragAsObservable().Subscribe(dragEvent => {}); mImage.OnPointerClickAsObservable().Subscribe(clickEvent => {});
UniRx对协程的支持
UniRx 对 Unity 的 Coroutine 也提供⽀持,可以将⼀个 Coroutine 转化为事件源(Observable)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32/**************************************************** 文件:RxCoroutineTest.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 17:36:21 功能:UniRx对协程的支持 *****************************************************/ using UnityEngine; using UniRx; using UniRx.Triggers; using System.Collections; namespace UniRxLesson { public class RxCoroutineTest : MonoBehaviour { IEnumerator CoroutineA() { yield return new WaitForSeconds(1.0f); Debug.Log("A"); } void Start() { Observable.FromCoroutine(_ => CoroutineA()) .Subscribe(_ => { // do something print("666"); }).AddTo(this); } } }
当然也⽀持将 Observable 转化为⼀个 Coroutine 中的 yield 对象
⽐如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/**************************************************** 文件:Rx2YieldTest.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/10 17:40:15 功能: Observable 转化为⼀个 Coroutine 中的 yield 对象 *****************************************************/ using UnityEngine; using UniRx; using System; using System.Collections; using UniRx.Triggers; namespace UniRxLesson { public class Rx2YieldTest : MonoBehaviour { IEnumerator Delay1Second() { yield return Observable.Timer(TimeSpan.FromSeconds(1.0f)).ToYieldInstruction(); Debug.Log("B"); } void Start() { StartCoroutine(Delay1Second()); } } }
WhenAll操作符
WhenAll在协程上的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/**************************************************** 文件:WhenAllCoroutineExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 9:39:56 功能:UniRx when all操作符 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; namespace UniRxLesson { public class WhenAllCoroutineExample : MonoBehaviour { IEnumerator A() { yield return new WaitForSeconds(1.0f); Debug.Log("A"); } IEnumerator B() { yield return new WaitForSeconds(2.0f); Debug.Log("B"); } void Start() { var aStream = Observable.FromCoroutine(_ => A()); var bStream = Observable.FromCoroutine(_ => B()); Observable.WhenAll(aStream, bStream) .Subscribe(_ => { print("C"); }).AddTo(this); } } }
WhenAll在按钮点击上的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/**************************************************** 文件:ButtonAllClickedOnce.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 9:44:40 功能:所有按钮一次点击后事件 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using UnityEngine.UI; namespace UniRxLesson { public class ButtonAllClickedOnce : MonoBehaviour { [SerializeField] Button mButtonA; [SerializeField] Button mButtonB; [SerializeField] Button mButtonC; void Start() { var aStream = mButtonA.OnClickAsObservable().First(); var bStream = mButtonB.OnClickAsObservable().First(); var cStream = mButtonC.OnClickAsObservable().First(); Observable.WhenAll( aStream, bStream, cStream) .Subscribe(_ => { Debug.Log("clicked"); }).AddTo(this); } } }
事件流的结束 OnCompleted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45/**************************************************** 文件:onCompletedExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 10:40:13 功能:事件流的结束 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using System; namespace UniRxLesson { public class onCompletedExample : MonoBehaviour { private void Start() { /*Observable.Timer(TimeSpan.FromSeconds(1.0f)).Subscribe(_=> print("OnNext after 1 Sec "), ()=> print("onComplete") );*/ //Observable.EveryUpdate().First().Subscribe(_ => //{ // Debug.Log("OnNext:First"); //}, () => //{ // Debug.Log("OnCompleted"); //}).AddTo(this); Observable.FromCoroutine(A) .Subscribe(_ => { Debug.Log("OnNext:"); }, () => { Debug.Log("OnCompleted:"); }); } IEnumerator A() { yield return new WaitForSeconds(2.0f); } } }
协程有Oncompleted事件,EveryUpdate没有,只有OnNext事件,默认Subscribe后面触发的是OnNext事件。正因为有OnCompleted事件,才有WhenAll操作。
Start:让多线程更简单
多线程,是作为⾼级开发者必须具备的⼀种技术。了解了多线程可以让我们充分利⽤多核移动端的计
算优势,也可以让我们的游戏体验更平滑。
在 Unity 中我们⼀般⽤ Thread.Start 开启⼀个线程。当逻辑⾮常复杂的时候多线程⾮常难以管理。
⽽ UniRx 改善了这⼀种状况。
⼀个”当所有线程运⾏完成后,在主线程执⾏某个任务” 这个功能,使⽤ UniRx 实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/**************************************************** 文件:ThreadExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 11:22:17 功能:UniRx管理多线程 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using System; using System.Threading; namespace UniRxLesson { public class ThreadExample : MonoBehaviour { private void Start() { var threadAStream = Observable.Start(() => { Thread.Sleep(TimeSpan.FromSeconds(1.0f)); return 10; }); var threadBStream = Observable.Start(() => { Thread.Sleep(TimeSpan.FromSeconds(3.0f)); return 10; }); Observable.WhenAll(threadAStream, threadBStream) .ObserveOnMainThread() .Subscribe(results => print(results[0] + ":" + results[1])); } } }
3 秒后,输出的结果如下:
10:10
这⾥有两个新的 API,⼀个是 Observable.Start,这个 API 意思开启⼀个线程流。
ObserveOnMainThread,意思是把 WhellAll 结果转到主线程上。这样 Subscribe ⾥的回调就可以使⽤ Unity 的 API 了(Unity 的很多 API 不可以在其他线程中使⽤ )。
使⽤ UniRx 来处理线程逻辑⾮常简单。
线程和 Coroutine (协程)都可以使⽤ WhenAll 这种操作符。
ObservableWWW 优雅的⽹络请求操作
以往我们不管使⽤ WWW 还是 UnityWebRequest 都要使⽤ Coroutine 去驱动。
但是使⽤协程写出来的代码,需要⼀堆判断,导致代码⾮常混乱。
⽽ UniRx 则是以以往⼀样简练的⻛格提供了对⽹络请求的⽀持。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30/**************************************************** 文件:WWWExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 11:49:55 功能:UniRx优雅的网络请求操作 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using System; namespace UniRxLesson { public class WWWExample : MonoBehaviour { private void Start() { ObservableWWW.Get("baidu.com") .Subscribe(responseText => { print(responseText.Substring(0, 10)); }, e=> { Debug.LogError(e); }); } } }
支持whenAll操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/**************************************************** 文件:WWWWhenAllExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 11:55:21 功能:WWW 中的WhenALL操作 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using System; namespace UniRxLesson { public class WWWWhenAllExample : MonoBehaviour { private void Start() { var aStream = ObservableWWW.Get("baidu.com"); var bStream = ObservableWWW.Get("sikiedu.com"); Observable.WhenAll(aStream, bStream) .Subscribe(responseText => { print(responseText[0].Substring(0, 10)); print(responseText[1].Substring(0, 100)); }); } } }
下载文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34/**************************************************** 文件:ProgressExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 12:24:0 功能:用UniRx下载文件,并且显示进度 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using System; namespace UniRxLesson { public class ProgressExample : MonoBehaviour { private void Start() { var progressObserable = new ScheduledNotifier<float>(); ObservableWWW.GetAndGetBytes("http://liangxiegame.com/media/QFramework_v0.0.9.unitypackage", progress: progressObserable) .Subscribe(bytes => { }, e=> { Debug.Log(e); }); progressObserable.Subscribe(progress => { Debug.LogFormat("进度为:{0}", progress); }); } } }
ReactiveCommand
我们先来看下 ReactiveCommand 定义 :
1
2
3
4
5
6public interface IReactiveCommand<T> : IObservable<T> { IReadOnlyReactiveProperty<bool> CanExecute { get; } bool Execute(T parameter); }
它提供了两个 API:
• CanExecte
• Execute
Execute ⽅法是被外部调⽤的。也就是这个 Command 的执⾏。这个很容易理解,只要外部调⽤的
Execute 就会执⾏。
⽽ CanExecute 则是内部使⽤的,并且对外部提供了只读访问。
当 CanExecute 为 false 时,在外部调⽤ Execute 则该 Command 不会被执⾏。
当 CanExecute 为 true 时,在外部调⽤ Execute 则该 Command 会被执⾏。
是什么决定 CanExecute 为 false 或 true 呢?
答案是其他的 Observable。
新创建的 ReactiveCommand 默认 CanExecute 为 true。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/**************************************************** 文件:ReactiveCommandExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 14:45:40 功能:ReactiveCommand示例 *****************************************************/ using UnityEngine; using UniRx; using System.Collections; using System; namespace UniRxLesson { public class ReactiveCommandExample : MonoBehaviour { private void Start() { var reactiveCommand = new ReactiveCommand(); reactiveCommand.Subscribe(_ => { print("执行"); }); reactiveCommand.Execute(); reactiveCommand.Execute(); reactiveCommand.Execute(); } } }
输出的结果是:
执行
执行
执行
⾮常地简单,只要调⽤ Execute。command 就会通知 Subscribe 的回调(因为 CanExecute 为 true)。
CanExecute 的开启关闭是由 Observable (事件源)决定的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/**************************************************** 文件:MouseDownUPExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 14:49:9 功能:熟悉ReactiveCommand中的CanExcuse属性 *****************************************************/ using UnityEngine; using UniRx; using UniRx.Triggers; using System.Collections; using System; namespace UniRxLesson { public class MouseDownUPExample : MonoBehaviour { private void Start() { var leftMouseDownStream = this.UpdateAsObservable(). Where(_ => Input.GetMouseButtonDown(0)) .Select(_ => true); var leftMouseUPStream = this.UpdateAsObservable(). Where(_ => Input.GetMouseButtonUp(0)) .Select(_ => false); var isMouseDown = Observable.Merge(leftMouseDownStream, leftMouseUPStream); var reactiveCommand = new ReactiveCommand(isMouseDown, false); reactiveCommand.Subscribe(x => { print("reactiveCommand"+x.ToString()); }); this.UpdateAsObservable().Subscribe(_ => reactiveCommand.Execute()); } } }
在上面的示例代码中,鼠标按下事件通过Select操作符让其Observable返回一个true,鼠标抬起事件通过Select操作符让其Observable事件源返回一个false。用Merge操作符将两个事件源合并成一个事件源作为ReactiveCommand的监听事件源,所以reactiveCommand中的CanExcuse属性则有合并后的事件源isMouseDown的返回值决定。当鼠标按下时,返回true,每帧调用的Excuse函数会被执行,输出字符串,抬起时,每帧调用的Excuse函数不会被执行,就不会在控制台输出字符串。
ReactiveCollection 与 ReactiveDictionary
ReactiveCollection
ReactiveCollection 类似于 List。
我们可以使⽤如下的操作符:
ObserverAdd // 当 新的 Item 添加则会触发
ObserverRemove // 删除
ObserverReplace // 替换(Update)
ObserverMove // 移动
ObserverCountChanged // 数量有改变(Add、Remove)
ReactiveCollection 示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41/**************************************************** 文件:ReactiveCollectionExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 15:36:55 功能: ReactiveCollection示例 *****************************************************/ using UnityEngine; using UniRx; namespace UniRxLesson { public class ReactiveCollectionExample : MonoBehaviour { ReactiveCollection<int> mAges = new ReactiveCollection<int> { 1,2,3,4,5}; void Start() { mAges.ObserveAdd() .Subscribe(addAge => { Debug.LogFormat("add:{0}",addAge); }); mAges.ObserveRemove() .Subscribe(removedAge => { Debug.LogFormat("remove:{0}",removedAge); }); mAges.ObserveCountChanged() .Subscribe(count => { Debug.LogFormat("count:{0}",count); }); foreach (var age in mAges) { Debug.Log(age); } mAges.Add(6); mAges.Remove(2); } } }
输出结果为
1
2
3
4
5
add:Index:5 Value:6
count:6
remove:Index:1 Value:2
count:5
ReactiveDictionary
ReactiveDictionary 功能与 Dictionary ⼀样。
同样地,它⽀持了⼏个操作符:
ObserverAdd // 当 新的 Item 添加则会触发
ObserverRemove // 删除
ObserverReplace // 替换(Update)
ObserverMove // 移动
ObserverCountChanged // 数量有改变(Add、Remove)
示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/**************************************************** 文件:ReactiveDictionaryExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 15:40:0 功能:ReactiveDictionary示例 *****************************************************/ using UniRx; using UnityEngine; namespace UniRxLesson { public class ReactiveDictionaryExample : MonoBehaviour { private ReactiveDictionary<string, string> mLanguageCode = new ReactiveDictionary<string, string>(){ {"en","英语"},{"cn","中⽂"}}; // Use this for initialization void Start() { mLanguageCode.ObserveAdd() .Subscribe(addedLanguage => { Debug.LogFormat("add:{0}", addedLanguage.Value); }); mLanguageCode.ObserveRemove() .Subscribe(removedLanguage => { Debug.LogFormat("remove:{0}", removedLanguage.Value); }); mLanguageCode.ObserveCountChanged() .Subscribe(count => { Debug.LogFormat("count:{0}", count); }); mLanguageCode.Add("jp", "⽇语"); mLanguageCode.Remove("en"); } } }
输出结果为
add:⽇语
count:3
remove:英语
count:2
加载场景 AsyncOperation
我们在异步加载资源或者异步加载场景的时候往往会⽤到 AsyncOperation。
UniRx 对 AsyncOperation 做了⽀持。使得加载操作可以很容易地监听加载进度。
示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34/**************************************************** 文件:AsyncOperationExample.cs 作者:Paul 邮箱: 794451358@qq.com 日期:2019/10/11 15:48:35 功能:用UniRx进行异步操作 *****************************************************/ using UnityEngine; using UniRx; using UnityEngine.SceneManagement; namespace UniRxLesson { public class AsyncOperationExample : MonoBehaviour { private void Start() { var progressObserable = new ScheduledNotifier<float>(); SceneManager.LoadSceneAsync(0).AsAsyncOperationObservable(progress: progressObserable).Subscribe(asyncOperation => { print("load Done"); asyncOperation.allowSceneActivation = true; Resources.LoadAsync<GameObject>("TestCanvas").AsAsyncOperationObservable(progressObserable).Subscribe(resourceRequest => { GameObject.Instantiate(resourceRequest.asset); }); progressObserable.Subscribe(progress => { Debug.LogFormat("已经加载了{0}", progress); }); }); } } }
最后
以上就是狂野小天鹅最近收集整理的关于UniRx入门纲要UniRx学习笔记的全部内容,更多相关UniRx入门纲要UniRx学习笔记内容请搜索靠谱客的其他文章。
发表评论 取消回复