课程地址 : http://www.sikiedu.com/course/271
凉鞋大大的,可以的话大家多支持一波~
Observable 是⼀条事件流,利用Observable可调用unity生命周期:
Observable.EveryFixedUpdate().Subscribe(_ => {});
Observable.EveryEndOfFrame().Subscribe(_ => {});
Observable.EveryLateUpdate().Subscribe(_ => {});
Observable.EveryAfterUpdate().Subscribe(_ => {});
除了 Update 还⽀持其他的事件,⽐如 ApplicationPause,Quit 等。
Observable.EveryApplicationPause().Subscribe(paused => {});
Observable.EveryApplicationFocus().Subscribe(focused => {});
Observable.EveryApplicationQuit().Subscribe(_ => {});
Observable.EveryUpdate() 这个 API 有的时候在某个脚本中实现,需要绑定 MonoBehaviour 的⽣命周
期(主要是 OnDestroy),当然也有的时候是全局的,⽽且永远不会被销毁的。
需要绑定 MonoBehaviour ⽣命周期的 EveryUpdate。只需要⼀个 AddTo 就可以进⾏绑定了。⾮常简
单,代码如下:
Observable.EveryUpdate()
.Subscribe(_ => {})
.AddTo(this);
需要绑定生命周期的Observable还有更简洁的实现,就是Trigger ,例如 :
this.OnDestroyAsObservable()
.Subscribe(_ => {});
Trigger 类型的 Observable 和我们之前讲的所有的 Observable 在表现上有⼀点⼀点不⼀样:
1. Trigger ⼤部分都是都是 XXXAsObsrevable 命名形式的。
2. 在使⽤ Trigger 的 GameObject 上都会挂上对应的 Observable XXXTrigger.cs 的脚本
Observable所调用的AddTo() 这个 API 其实是封装了⼀种 Trigger: ObservableDestroyTrigger。
顾名思义,就是当 GameObject 销毁时获取事件的⼀个触发器。
⼀般的 Trigger 都会配合 MonoBehaviour ⼀起使⽤。
各种细分类型的 Update:
this.FixedUpdateAsObservable().Subscribe(_ => {});
this.LateUpdateAsObservable().Subscribe(_ => {});
this.UpdateAsObservable().Subscribe(_ => {});
还有各种碰撞的 Trigger:
this.OnCollisionEnterAsObservable().Subscribe(collision => {});
this.OnCollisionExitAsObservable().Subscribe(collision => {});
this.OnCollisionStayAsObservable().Subscribe(collision => {});
// 同样 2D 的也⽀持
this.OnCollision2DEnterAsObservable().Subscribe(collision2D => {});
this.OnCollision2DExitAsObservable().Subscribe(collision2D => {});
this.OnCollision2DStayAsObservable().Subscribe(collision2D => {});
⼀些脚本的参数监听:
this.OnEnableAsObservable().Subscribe(_ => {});
this.OnDisableAsObservable().Subscribe(_ => {});
除了 MonoBehaviour ,Trigger 也⽀持了其他组件类型,⽐如 RectTransform、Transform、UIBehaviour 等等。
UIBehaivour 是 UGUI 所有控件的基类。 因此Trigger对UIBehaivour 的各种事件提供⽀持。
⽐如所有的 Graphic 类型(Graphic 简单介绍下,所有的在 Inspector 上显示,Raycast Target 选定框的都是 Graphic 类型)都⽀持 OnPointerDownAsObservable、OnPointerEnterAsObservable、OnPointerEnterAsObservable 等 Trigger。我们知道,如果想⾃⼰去接收⼀个 OnPointerDown 事件,需要实现⼀个 IPointerDownHandler 接⼝,⽽ UniRx 则把 所有的 IXXXHandler 接⼝都做成 Trigger了。这样再也不⽤需要⽹上到处流传的 UIEventListener.Get(gameObejct).onClick 这种⽅式了。因为这种⽅式问题很多,⽐如,由于它继承了 EEventTriggers,实现了所有的事件接⼝,他就会吞噬掉OnScroll 等事件.
需要⼀个全部实现并且吞并事件的版本也没关系,UniRx 也实现了⼀个 ObservableEventTrigger。和UIEventListener ⼀样的。
具体可以参考 ObservableTriggerExtensions.Component.cs
[SerializeField] Button mButton;
[SerializeField] Toggle mToggle;
[SerializeField] Scrollbar mScrollbar;
[SerializeField] ScrollRect mScrollRect;
[SerializeField] Slider mSlider;
[SerializeField] InputField mInputField;
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));
}
Text resultText = GetComponent();
mInputField.OnValueChangedAsObservable().SubscribeToText(resultText);
mImage.OnBeginDragAsObservable().Subscribe(dragEvent => {});
mGraphic.OnDragAsObservable().Subscribe(dragEvent => {});
mText.OnEndDragAsObservable().Subscribe(dragEvent => {});
mImage.OnPointerClickAsObservable().Subscribe(clickEvent => {});
UnityEvent mEvent;
void Start()
{
mEvent.AsObservable()
.Subscribe(_ =>
{
// process event
});
}
这不是全部,只是做个简介
可以替代⼀切变量,给变量创造了很多功能。
假如我们想监听⼀个值是否发⽣了改变。
public int Age
{
get
{
...
}
set
{
if (mAge != value)
{
mAge = value;
// send event
OnAgeChanged();
// call delegate
}
}
}
public void OnAgeChanged()
{
}
这样在类的内部,写⼀次 OnAgeChanged 是没问题的。但是我想在这个类的外部监听这个值的改变,
那就要声明⼀个委托来搞定了。委托的维护成本⽐较低,是可以接受的,直到发现了 UniRx 的ReactiveProperty。就再也不想⽤委托来做这种⼯作了
public class ReactivePropertyExample : MonoBehaviour
{
public IntReactiveProperty Age = new IntReactiveProperty(0);
void Start()
{
Age.Subscribe(age =>
{
Debug.Log("inner received age changed");
});
Age.Value = 10;
}
}
public class PersonView
{
ReactivePropertyExample mReactiveProeprtyExample;
void Init()
{
mReactiveProeprtyExample.Age.Subscribe((age) =>
{
Debug.Log(age);
});
}
}
当任何时候,Age 的值被设置,就会通知所有 Subscribe 的回调函数。
⽽ Age 可以被 Subscribe 多次的。
并且同样⽀持 First、Where 等操作符。
MVP 设计模式 : Model-View-(Reactive)Presenter Pattern
在 Ctrl 中,进⾏ Model 和 View 的绑定。Model 的所有属性都是⽤ ReactiveProperty,然后在 Ctrl 中进⾏订阅。
通过 View 更改 Model 的属性值。形成⼀个 View->Ctrl->Model->Ctrl->View 这么⼀个事件响应环。
参考以下案例,可以看出UniRx可以迎送实现MVC模式:
public class EnemyExample : MonoBehaviour
{
[SerializeField] EnemyModel mEnemy = new EnemyModel(200);
void Start()
{
var attackBtn = transform.Find("Button").GetComponent
在 Unity ⾥,序列化是⼀个很重要的功能,如果不可序列化,则在编辑器上就看不到参数。⽽ReactiveProperty 是泛型的,序列化起来⽐较麻烦。为了解决这个问题,UniRx ⽀持了可序列化的ReactiveProperty 类型,⽐如 Int/LongReactivePropety、Float/DoubleReactiveProperty、StringReactiveProperty、BoolReactiveProperty,还有更多,请参见InspectableReactiveProperty.cs。
如果你需要 [Multiline] 或者[Range] 添加到 ReactiveProperty 上,你可以使⽤MultilineReactivePropertyAttribute 和 RangeReactivePropertyAttribute 替换 Multiline 和 Range。
这些 InspectableReactiveProperties 可以在 inspector ⾯板显示,并且当他们的值发⽣变化时发出通知,甚⾄在编辑器⾥变化也可以。
这里提一下MVVM :
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
这里没有实现MVVM的原因是Unity 没有提供 UI 绑定机制,创建⼀个绑定层过于复杂并且会对性能造成影响(使⽤反射)。尽管如此,视图还是需要更新。Presenters 层知道 View 组件并且能更新它们