UniRx精講(一):UniRx簡介&定時功能實現

1.UniRx 簡介

UniRx 是一個 Unity3D 的編程框架。它專一於解決時間上異步的邏輯,使得異步邏輯的實現更加簡潔和優雅。git

簡潔優雅如何體現?

好比,實現一個「只處理第一次鼠標點擊事件」這個功能,使用 UniRx 實現以下:github

Observable.EveryUpdate()
			.Where(_ => Input.GetMouseButtonUp(0))
			.First()
			.Subscribe(_ => { // do something   });

代碼作的事情很簡單:編程

  1. 開啓一個 Update 的事件監聽。
  2. 每次 Update 事件被調用時,進行鼠標是否擡起的判斷。
  3. 若是判斷經過,則進行計數,而且只獲取第一次點擊事件。
  4. 訂閱/處理事件。

若是在 Unity 中,使用傳統的方式實現如上功能,首先要建立一個成員變量來記錄點擊次數/是否點擊過,而後在腳本中建立一個 Update 方法來監聽鼠標擡起的事件。設計模式

若是在 Update 方法中,除了實現鼠標事件監聽這個功能以外,還要實現其餘的功能。那麼 Update 裏就會充斥着大量的狀態判斷等邏輯。代碼很是不容易閱讀。網絡

而 UniRx 提供了一種編程思惟,使得平時一些比較難以實現的異步邏輯(好比以上這種),使用 UniRx 輕鬆搞定,而且不失代碼的可讀性。架構

固然 UniRx 的強大不只僅如此。框架

它還能夠:異步

  • 優雅實現 MVP(MVC)架構模式。
  • 對 UGUI/Unity API 提供了加強,不少須要寫大量代碼的 UI 邏輯,使用 UniRx 優雅實現。
  • 輕鬆完成很是複雜的異步任務處理。
  • ......

最最重要的是,它能夠提升咱們的編碼效率,同時還給咱們的大腦提供一個強有力的編程模型。而 UniRx 自己的源碼很是值得研究學習,就連大名鼎鼎的 uFrame 框架,在 1.6 版本以後,使用 UniRx 進行了大幅重構,其事件/數據綁定層使用 UniRx 強力驅動。學習

筆者的 QFramework 也一樣引入了 UniRx,有一大批的框架用戶都是由於支持了 UniRx 慕名而來。動畫

爲何要用 UniRx ?

UniRx 就是 Unity 版本的 Reactive Extensions,Reactive Extensions 中文意思是:響應式擴展,響應式指的是觀察者和定時器,擴展指的是 LINQ 的操做符。Reactive Extensions 以擅長處理時間上異步的邏輯、以及極簡的 API 風格而聞名。

咱們都知道,遊戲不少的系統都是在時間上異步的,因此 Unity 開發者所須要實現的異步邏輯是很是多的。這也是爲何 Unity 官方在引擎層實現了 Coroutine(協程)這樣的概念。

在遊戲中,像動畫的播放、聲音的播放、網絡請求、資源加載/卸載、Tween 動畫、場景過渡等都是在時間上異步的邏輯。甚至是遊戲循環(Every Update、OnCollisionEnter 等)、傳感器數據(Kinect、Leap Motion、VR Input 等)都是時間上異步的邏輯(事件)。

當咱們在項目中實現以上的邏輯的時候,每每使用的方式是用大量的回調實現,最終隨着項目的擴張會致使傳說中的」回調地獄」。

相對較好的方法則是使用消息/事件進行實現,結果致使「消息滿天飛」,致使代碼很是難以閱讀。

以上的任務使用 Coroutine 也是很是不錯的,可是 Coroutine 在 Unity 使用的時候,須要定義一個方法。寫起來是很是面向過程的。當邏輯稍微複雜一點,就很容易形成 Coroutine 嵌套 Coroutine,代碼是很是不容易閱讀的(強耦合)。

而 UniRx 的出現恰好解決了這個問題,它介於回調和事件之間。

它有事件的概念,只不過它的事件是像流水同樣流過來,而咱們要作的則是簡單地對這些事件進行組織、變換、過濾、合併就能夠獲得咱們想要的結果了。

它也用掉了回調,只不過它的回調是在事件通過組織以後,只須要調用一次就能夠進行事件處理了。

它的原理和 Coroutine (迭代器模式)、LINQ 很是類似,可是比 Coroutine 強大得多。

UniRx 將時間上異步的事件轉化爲響應式的事件序列,經過 LINQ操做能夠很簡單地組合起來。

爲何要用 UniRx? 答案就是遊戲自己有大量的在時間上異步的邏輯,而 UniRx 剛好擅長處理這類邏輯,使用 UniRx 能夠節省咱們的時間,同時讓代碼更簡潔易讀。

Rx 只是一套標準,其餘的語言也有實現,若是在 Unity 中熟悉了這套標準,那麼在將來,你們在作別的語言的項目的時候,很容易得到 Rx 的能力。

專欄內容:

  1. 實踐並講解開發中最經常使用的 UniRx API。
  2. 對 UniRx 進行一個全方面的簡介。
  3. 在每一個階段結束後就會結合所學的知識進行項目實踐。
  4. UniRx 操做符大全。
  5. UniRx 源碼閱讀。
  6. UniRx 背後的設計原理及設計模式學習。
  7. LINQ、Coroutine 底層原理剖析。
  8. BindingsRx、uFrame 源碼閱讀。

OK,讓咱們從下一篇開始,感覺一下 UniRx 的魅力吧。

知識地圖

image.png

2.定時功能實現

在 Unity 開發中,延時是咱們常常要實現的功能,這個功能對於有點經驗的開發者來講不難。

最多見的實現方式以下:

using UnityEngine;

public class CommonDelayExample : MonoBehaviour
{
	private float mStartTime;

	void Start()
	{
		mStartTime = Time.time;
	}

	void Update()
	{
		if (Time.time - mStartTime > 5)
		{
			DoSomething();
			// 避免再次執行
			mStartTime = float.MaxValue;
		}
	}

	void DoSomething()
	{
		Debug.Log("DoSomething");
	}
}

這是不少初學者剛入門時候的實現方式,而初學者們隨着深刻學習後來接觸到了 Coroutine(協程),使用 Coroutine 實現定時功能會更容易,並且也是更好的選擇,實現以下:

using System;
using System.Collections;
using UnityEngine;

public class CoroutineDelayExample : MonoBehaviour
{
	void Start()
	{
		StartCoroutine(Timer(5, DoSomething));
	}

	IEnumerator Timer(float seconds, Action callback)
	{
		yield return new WaitForSeconds(seconds);
		callback();
	}

	void DoSomething()
	{
		Debug.Log("DoSomething");
	}
}

協程已經把代碼精簡了不少,不過接下來有更厲害的,使用 UniRx。

代碼以下:

Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(_ => { /* do something */ });

固然以上代碼是沒有和 MonoBehaviour 進行生命週期綁定的,也就是說當 MonoBehaviour 銷燬了以後,這個定時邏輯可能還在執行,這樣就會有形成空指針異常的風險。

要解決也很簡單,代碼以下:

Observable.Timer(TimeSpan.FromSeconds(5))
		.Subscribe(_ => { /* do something */ })
		.AddTo(this);

只要加上一個 AddTo(this) 就能夠了。
這樣,當 this(MonoBehaviour) Destroy 的時候,這個延時邏輯也會銷燬掉,從而避免形成空指針異常。

三行代碼,寫下來大約 20 秒的時間,就搞定了一個實現起來比較麻煩的邏輯。

今天的內容就這些。

知識地圖

image.png

更多內容

相關文章
相關標籤/搜索