外面網(wǎng)站怎么做怎么創(chuàng)建一個(gè)屬于自己的網(wǎng)站
Reactive X 是 Reactive Extensions 的縮寫,一般簡寫為 Rx,最初是 LINQ 的一個(gè)擴(kuò)展,由微軟的團(tuán)隊(duì)開發(fā),Rx 是一個(gè)編程模型,目標(biāo)是提供一致的編程接口,幫助開發(fā)者更方便的處理異步數(shù)據(jù)流,支持大部分流行語言。
Rx是一個(gè)函數(shù)庫,讓開發(fā)者可以利用可觀察序列和 LINQ 風(fēng)格查詢操作符來編寫異步和基于事件的程序,使用Rx,開發(fā)者可以用Observables 表示異步數(shù)據(jù)流,用 LINO 操作符查詢異步數(shù)據(jù)流,用 Schedulers 參數(shù)化異步數(shù)據(jù)流的并發(fā)處理,Rx可以這樣定義︰
Rx = Observables + LINQ + Schedulers
Rx 結(jié)合了觀察者模式、迭代器模式和函數(shù)式編程的精華。UniRx 就是 Unity Reactive Extensions,是為 Unity 平臺設(shè)計(jì)的響應(yīng)式編程框架。
在游戲中,?部分的邏輯都是在時(shí)間上異步的。?如動畫的播放、聲?的播放、?絡(luò)請求、資源加載/卸載、場景過渡等。
在實(shí)現(xiàn) 異步邏輯時(shí)經(jīng)常?到?量的回調(diào),最終隨著項(xiàng)?的擴(kuò)張導(dǎo)致傳說中的”回調(diào)地獄”。
相對較好地?法則是使?消息/事件的發(fā)送,結(jié)果導(dǎo)致“消息滿天?”,導(dǎo)致代碼?常難以閱讀。
? UniRx 的出現(xiàn)剛好解決了這個(gè)問題,它介于回調(diào)和事件之間。
常用API
監(jiān)聽 mono 生命周期函數(shù)
Observable.EveryUpdate() // 開啟 Update 的事件監(jiān)聽.Subscribe(_ => // 訂閱/處理事件{Debug.Log("Update");});Observable.EveryLateUpdate().Subscribe(_ =>{Debug.Log("LateUpdate");});Observable.EveryFixedUpdate().Subscribe(_ =>{Debug.Log("FixedUpdate");});Observable.EveryEndOfFrame().Subscribe(_ =>{Debug.Log("EndOfFrame");});Observable.EveryApplicationFocus().Subscribe(focuse =>{Debug.Log("Focus: " + focuse);});
如果在 Update ?法中,除了實(shí)現(xiàn)?標(biāo)事件監(jiān)聽這個(gè)功能之外,還要實(shí)現(xiàn)其他的功能。那么 Update ?就會充斥著?量的狀態(tài)判斷等邏輯。代碼?常不容易閱讀。
? UniRx 提供了?種編程思維,使得平時(shí)?些?較難實(shí)現(xiàn)的異步邏輯,使? UniRx 輕松搞定,并且不失代碼的可讀性
參數(shù)下劃線 _ 表示幀數(shù),這里用不到就用 _ 表示
定時(shí)功能
Observable.Timer(TimeSpan.FromSeconds(5.0f)).Subscribe(_ =>{Debug.Log("do something");});
AddTo 與 Trigger
Observable.Timer(TimeSpan.FromSeconds(2.0f)).Subscribe(_ =>{Debug.Log("延時(shí)兩秒"); }).AddTo(this);this.OnDestroyAsObservable().Subscribe(_ =>{Debug.Log("OnDestroy");});this.OnCollisionEnterAsObservable().Subscribe(collision =>{Debug.Log("CollisionEnter");});
AddTo 會在 GameObject 上添加一個(gè) ObservableDestroyTrigger 腳本監(jiān)聽它的 OnDestroy 事件,當(dāng) GameObject 銷毀時(shí)也會銷毀正在運(yùn)行的 UniRx 任務(wù),即 GameObject 銷毀時(shí),這個(gè) Timer 也會銷毀,避免空引用異常。
ObservableDestroyTrigger 是一個(gè) Trigger,Trigger 大部分都是都是 XXXAsObsrevable 命名形式的。
在使用 Trigger 的 GameObject 上都會掛上對應(yīng)的 ObservableXXXTrigger 的腳本,AddTo 這個(gè) API 就是封裝了 ObservableDestroyTrigger
Where,First 過濾
Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0)) //進(jìn)行一個(gè)鼠標(biāo)是否點(diǎn)擊的判斷.First() //只獲取第一次點(diǎn)擊事件.Subscribe(_ =>{Debug.Log("left mouse clicked");});//也可以寫成這樣
Observable.EveryUpdate().First(_ => Input.GetMouseButtonDown(0)) //只獲取第一次點(diǎn)擊事件.Subscribe(_ =>{Debug.Log("left mouse clicked");});
EveryUpdate 是事件的發(fā)布者。他會每幀會發(fā)送?個(gè)事件過來。
Subscribe 是事件的接收者,接收的是 EveryUpdate 發(fā)送的事件。
Where 則是在事件的發(fā)布者和接收者之間的?個(gè)過濾操作。會過濾掉不滿?條件的事件
First 又進(jìn)行了一個(gè)過濾
Merge
在 UniRx 世界中,任何東西都是以事件流的形式存在,EveryUpdate 和 Timer 都是開啟一條事件流。UniRx 可以開啟多條事件流,然后使用 Merge 合并。
private void Start()
{var leftClickEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0));var rightClickEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(1));//點(diǎn)擊左鍵或右鍵都會觸發(fā)Observable.Merge(leftClickEvents, rightClickEvents).Subscribe(_ =>{Debug.Log("mouse clicked");});
}
WhenAll
當(dāng)所有事件流都結(jié)束后觸發(fā)
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(_ =>{Debug.Log("全部處理完");});
}
Start
開啟線程
void Start()
{var threadAStream = Observable.Start(() =>{Thread.Sleep(TimeSpan.FromSeconds(1));return 10;});var threadBStream = Observable.Start(() =>{Thread.Sleep(TimeSpan.FromSeconds(3));return 10;});Observable.WhenAll(threadAStream, threadBStream).ObserveOnMainThread() //轉(zhuǎn)到主線程.Subscribe(results =>{Debug.LogFormat("{0}:{1}", results[0], results[1]);});
}
UGUI 支持
public Button button;
public Toggle toggle;
public Image image;
public Slider slider;
public InputField inputField;void Start()
{button.OnClickAsObservable().Subscribe(_ =>{Debug.Log("button clicked");});toggle.OnValueChangedAsObservable().Subscribe(on =>{Debug.LogFormat("toggle value changed: {0}", on);});image.OnDragAsObservable().Subscribe(_ =>{Debug.Log("dragging");});slider.OnValueChangedAsObservable().Subscribe(value =>{Debug.Log("slider: " + value);});inputField.OnValueChangedAsObservable().Subscribe(value =>{Debug.Log("input: " + value);});inputField.OnEndEditAsObservable().Subscribe(value =>{Debug.Log("input: " + value);});
}
查看源碼,button.onClick 是 ButtonClickedEvent 類型,而 ButtonClickedEvent 繼承自 UnityEvent, button.OnClickAsObservable 是對點(diǎn)擊事件進(jìn)行了封裝,同理 UniRx 對其他組件的事件進(jìn)行了封裝
public static IObservable<Unit> OnClickAsObservable(this Button button)
{return button.onClick.AsObservable();
}
public static IObservable<Unit> AsObservable(this UnityEngine.Events.UnityEvent unityEvent)
{return Observable.FromEvent<UnityAction>(h => new UnityAction(h), h => unityEvent.AddListener(h), h => unityEvent.RemoveListener(h));
}
public static IObservable<Unit> FromEvent<TDelegate>(Func<Action, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
{return new FromEventObservable<TDelegate>(conversion, addHandler, removeHandler);
}
綁定兩個(gè)組件
public Toggle mToggle;
public Button mButton;
public Slider mSlider;
public Text mText;void Start()
{// 當(dāng) mToggle.isOn = true 時(shí),mButton.interactable = truemToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);// 滑動條變化時(shí)更新文本mSlider.OnValueChangedAsObservable().SubscribeToText(mText, x => x.ToString());
}
Select
Select 是轉(zhuǎn)換操作符,用于將一個(gè)事件源中的值轉(zhuǎn)換成另一個(gè)類型的值
public Button buttonA;
public Button buttonB;private void Start()
{// Select將空參數(shù)Unit轉(zhuǎn)換為stringbuttonA.OnClickAsObservable().Select(_ => "A").Subscribe(btnId =>{Debug.LogFormat("button {0} clicked", btnId);});buttonB.OnClickAsObservable().Select(_ => "B").Subscribe(btnId =>{Debug.LogFormat("button {0} clicked", btnId);});
}
public ReactiveCollection<int> IntList = new ReactiveCollection<int>();void Start()
{// Select 把每個(gè)值進(jìn)行平方IntList.ObserveAdd().Select(x => x.Value * x.Value).Subscribe(value =>{Debug.Log("add:" + value);});// 添加后觸發(fā)事件IntList.Add(10);
}
ReactiveProperty(響應(yīng)式屬性)
監(jiān)聽屬性的變化,發(fā)送通知
// 0 是默認(rèn)值,可序列化展示在面板上
public IntReactiveProperty Age = new IntReactiveProperty(0);
// 泛型寫法,序列化很麻煩
public ReactiveProperty<string> Name = new ReactiveProperty<string>("Tom");void Start()
{Age.Subscribe(age =>{Debug.Log("inner received age changed");});// 賦值后觸發(fā)事件Age.Value = 10;
}
ReactiveCollection(響應(yīng)式集合)
ReactiveCollection 類似于 List,監(jiān)聽集合的變化,發(fā)送通知
public ReactiveCollection<int> IntList = new ReactiveCollection<int>();void Start()
{IntList.ObserveAdd().Subscribe(value => Debug.Log("add:" + value));IntList.ObserveRemove().Subscribe(value => Debug.Log("remove:" + value));IntList.ObserveCountChanged().Subscribe(count => Debug.Log("count:" + count));// 添加后觸發(fā)事件IntList.Add(10);
}
ReactiveDictionary(響應(yīng)式字典)
ReactiveDictionary<string, string> mLanguageCode = new ReactiveDictionary<string, string>
{{"cn","中文"},{"en","英文"}
};void Start()
{mLanguageCode.ObserveAdd().Subscribe(addedLanguage => Debug.LogFormat("add:{0}", addedLanguage));mLanguageCode.ObserveRemove().Subscribe(removedLanguage => Debug.LogFormat("remove:{0}", removedLanguage));mLanguageCode.ObserveCountChanged().Subscribe(count => Debug.LogFormat("count:{0}", count));mLanguageCode.Add("jp", "日文");mLanguageCode.Remove("en");
}
MVP
View 依賴于 Presenter:View通過Presenter來獲取數(shù)據(jù)并處理用戶輸入。
Presenter 依賴于 View 和 Model:負(fù)責(zé)處理View和Model之間的交互邏輯。
Model 不依賴于 View 或 Presenter:Model是獨(dú)立的業(yè)務(wù)邏輯和數(shù)據(jù)層,它只負(fù)責(zé)數(shù)據(jù)的存儲、處理和業(yè)務(wù)邏輯的實(shí)現(xiàn)。當(dāng)Model的狀態(tài)發(fā)生變化時(shí),它可能會通過回調(diào)或事件通知Presenter。
// Presenter
public class EnemyExample : MonoBehaviour
{// Viewpublic Button attackBtn;public Text HPText;EnemyModel mEnemy = new EnemyModel(200);void Start(){// UGUI組件和響應(yīng)式屬性綁定attackBtn.OnClickAsObservable().Subscribe(_ =>{mEnemy.HP.Value -= 99;});mEnemy.HP.SubscribeToText(HPText);mEnemy.IsDead.Where(isDead => isDead).Select(isDead => !isDead).SubscribeToInteractable(attackBtn);}
}// Model
public class EnemyModel
{public ReactiveProperty<long> HP;public IReadOnlyReactiveProperty<bool> IsDead;public EnemyModel(long initialHP){HP = new ReactiveProperty<long>(initialHP);IsDead = HP.Select(hp => hp <= 0).ToReactiveProperty();}
}
ReactiveCommand
用法類似命令模式,ReactiveCommand 實(shí)現(xiàn) IReactiveCommand<T> 接口
public interface IReactiveCommand<T> : IObservable<T>
{IReadOnlyReactiveProperty<bool> CanExecute { get; } //內(nèi)部使用,外部只讀bool Execute(T parameter); //外部調(diào)用的
}
當(dāng) CanExecute 為 true 時(shí),調(diào)用 Execute,Command 才會執(zhí)行,默認(rèn) CanExecute 為 true
void Start()
{// 默認(rèn) CanExecute 為 truevar reactiveCommand = new ReactiveCommand();reactiveCommand.Subscribe(_ =>{Debug.Log("每次Execute成功調(diào)用后執(zhí)行");});reactiveCommand.Execute();reactiveCommand.Execute();
}
泛型作為參數(shù)
void Start()
{var reactiveCommand = new ReactiveCommand<int>();reactiveCommand.Where(x => x % 2 == 0).Subscribe(x => Debug.LogFormat("{0}是偶數(shù)", x));reactiveCommand.Where(x => x % 2 != 0).Timestamp().Subscribe(x => Debug.LogFormat("{0}是奇數(shù),{1}", x.Value, x.Timestamp));reactiveCommand.Execute(10);reactiveCommand.Execute(11);
}
設(shè)置事件源
private void Start()
{var mouseDownStream = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0)).Select(_ => true);var mouseUpStream = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonUp(0)).Select(_ => false);var isMouseUp = Observable.Merge(mouseUpStream, mouseDownStream);// 設(shè)置事件源和 CanExecute 為 falsevar reactiveCommand = new ReactiveCommand(isMouseUp, false);reactiveCommand.Subscribe(_ =>{Debug.Log("鼠標(biāo)按下");});Observable.EveryUpdate().Subscribe(_ =>{reactiveCommand.Execute();});
}
AsyncOperation(異步操作)
加載資源
void Start()
{Resources.LoadAsync<GameObject>("資源名稱").AsAsyncOperationObservable().Subscribe(resourceRequest =>{Instantiate(resourceRequest.asset);});
}
加載場景
void Start()
{// 加載進(jìn)度var progressObservable = new ScheduledNotifier<float>();// 加載 Build Settings 中第 0 個(gè)場景SceneManager.LoadSceneAsync(0).AsAsyncOperationObservable(progressObservable).Subscribe(_ =>{Debug.Log("load done");});progressObservable.Subscribe(progress =>{Debug.LogFormat("加載了:{0}%", progress * 100);});
}
類 LINQ 操作符
Distinct
Distinct 意思是清晰的,不同的,用于查詢不重復(fù)的結(jié)果集
List<string> list = new List<string>
{"張三", "張三", "李四"
};list.ToObservable().Distinct().Subscribe(name =>{Debug.Log(name);});
Last
取列表最后一個(gè)元素,和 First 相反
class Student
{public string Name;public int Age;
}void Start()
{List<Student> students = new List<Student>{new Student{ Name = "張三", Age = 10 },new Student{ Name = "張三", Age = 15 },new Student{ Name = "李四", Age = 21 },};students.ToObservable().Last(student => student.Name == "張三").Subscribe(student =>{Debug.Log(student.Age);});
}
SelectMany
將一個(gè)序列中的每個(gè)元素投影到另一個(gè)序列,并將這些序列合并為一個(gè)單一的序列。具體來說,它會對每個(gè)元素進(jìn)行遍歷處理,然后將結(jié)果序列合并起來
List<string> list = new List<string>
{"123", "456"
};list.ToObservable().SelectMany(c => c).Subscribe(c =>{Debug.Log(c);});
輸出
1
2
3
4
5
6
還可以實(shí)現(xiàn)協(xié)程的順序執(zhí)行
void Start()
{var aStream = Observable.FromCoroutine(A);var bStream = Observable.FromCoroutine(B);aStream.SelectMany(bStream).Subscribe(_ => Debug.Log("A,B結(jié)束"));
}IEnumerator A()
{yield return new WaitForSeconds(1f);Debug.Log("A");
}IEnumerator B()
{yield return new WaitForSeconds(1f);Debug.Log("B");
}
未完待續(xù)