UniRx第一季学习(四) ------ Observable/Trigger类型的关键字/ReactiveProperty/MVP 实现

课程地址 : http://www.sikiedu.com/course/271

凉鞋大大的,可以的话大家多支持一波~


一.Observable类型

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);

二.Trigger类型的关键字

需要绑定生命周期的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 ⼀起使⽤。

1.unity生命周期Triggle

各种细分类型的 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 等等。 

2.UIBehaviour Triggle

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

①.Observable

[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));
}

②. Subscribe 

Text resultText = GetComponent<Text>();
mInputField.OnValueChangedAsObservable().SubscribeToText(resultText);

③.常用UI事件增强

mImage.OnBeginDragAsObservable().Subscribe(dragEvent => {});
mGraphic.OnDragAsObservable().Subscribe(dragEvent => {});
mText.OnEndDragAsObservable().Subscribe(dragEvent => {});
mImage.OnPointerClickAsObservable().Subscribe(clickEvent => {});

3.UnityEvent 

UnityEvent mEvent;
void Start()
{
   mEvent.AsObservable()
   .Subscribe(_ => 
   {
     // process event
   });
}

这不是全部,只是做个简介 

三.响应式属性ReactiveProperty

可以替代⼀切变量,给变量创造了很多功能。
假如我们想监听⼀个值是否发⽣了改变。

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 实现

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<Button>();
            var HPText = transform.Find("Text").GetComponent<Text>();

            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();
        }
    }

在 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 组件并且能更新它们

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值