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

1.UniRx 簡介

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

簡潔優雅如何體現?

比如,實現一個“只處理第一次鼠標點擊事件”這個功能,使用 UniRx 實現如下:

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 的魅力吧。

知識地圖

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 秒的時間,就搞定了一個實現起來比較麻煩的邏輯。

今天的內容就這些。

知識地圖

更多內容

  • QFramework 地址:https://github.com/liangxiegame/QFramework
  • QQ 交流群:623597263
  • 涼鞋的主頁:https://liangxiegame.com/zhuanlan
  • 關注公眾號:liangxiegame 獲取第一時間更新通知及更多的免費內容。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

您可能也會喜歡…