开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

教程分享

关注:762

当前位置:游戏蛮牛 技术专区 教程分享

查看: 767|回复: 1

[实例教程] UniRx - Unity响应式编程插件(4):MVP框架

[复制链接]  [移动端链接]

22

主题

24

帖子

64

积分

Rank: 2Rank: 2

UID
24181
好友
0
蛮牛币
213
威望
0
注册时间
2014-5-7
在线时间
28 小时
最后登录
2018-5-25
发表于 2018-2-7 21:24:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本文首发于“洪流学堂”微信公众号。
洪流学堂,让你快人几步成为Unity大牛!
本文译者:郑洪智 - 你的技术探路者
本文翻译自UniRx(https://github.com/neuecc/UniRx)插件的ReadMe
这个插件是我特别喜欢的一个插件,希望能将这种技术思想传播给大家
UniRx是什么?
UniRx (Unity响应式编程插件) 重写了.Net的响应式扩展。.Net官方的Rx很棒,但是在Unity中无法使用,并且与IOS的IL2CPP有兼容性问题。这个库这些问题并且添加了一些Unity专属的工具类。 支持的平台有:PC/Mac//iOS/WP8/WindowsStore/等等,并且支持Unity4.6之后的所有版本。
前情回顾
在UniRx - Unity响应式编程插件(1)[http://mp.weixin.qq.com/s/jvSYTplCYHeAL1XrATOeJQ]中讲了
  • 如何让你的代码更简洁
  • 如何简化网络操作
  • 如何和协程一起使用

在UniRx - Unity响应式编程插件(2)[http://mp.weixin.qq.com/s/fzQ3fU5CwE5DZayWbclIcw]中讲了
  • 让多线程更简单
  • 使用触发器让代码更简洁
  • Observable 生命周期管理

在UniRx - Unity响应式编程插件(3)[https://mp.weixin.qq.com/s/H7TIqbdtp5l2wTL-thwKIw]中讲了
  • 将Unity回调转为IObservable
  • 调试Debugging
  • 基于帧数的时间操作
  • 微协程

uGUI集成
UniRx处理UnityEvent很简单。使用UnityEvent.AsObservable订阅事件。
public Button MyButton;
// ---
MyButton.onClick.AsObservable().Subscribe(_ => Debug.Log("clicked"));
将事件转为Observables,这样就可以用声名式UI编程。
public Toggle MyToggle;
public InputField MyInput;
public Text MyText;
public Slider MySlider;

// On Start, you can write reactive rules for declaretive/reactive ui programming
void Start()
{
    // Toggle, Input etc as Observable (OnValueChangedAsObservable is a helper providing isOn value on subscribe)
    // SubscribeToInteractable is an Extension Method, same as .interactable = x)
    MyToggle.OnValueChangedAsObservable().SubscribeToInteractable(MyButton);

    // Input is displayed after a 1 second delay
    MyInput.OnValueChangedAsObservable()
        .Where(x => x != null)
        .Delay(TimeSpan.FromSeconds(1))
        .SubscribeToText(MyText); // SubscribeToText is helper for subscribe to text

    // Converting for human readability
    MySlider.OnValueChangedAsObservable()
        .SubscribeToText(MyText, x => Math.Round(x, 2).ToString());
}
更多的响应式UI编程参见工程中例子Sample12, Sample13以及下面ReactiveProperty部分。
ReactiveProperty, ReactiveCollection
游戏数据变化时通常需要通知别的类。我们应该用属性和事件(回调)么?这通常太繁琐了。UniRx提供了ReactiveProperty类,一个轻量的属性代理。
// Reactive Notification Model
public class Enemy
{
    public ReactiveProperty<long> CurrentHp { get; private set; }

    public ReactiveProperty<bool> IsDead { get; private set; }

    public Enemy(int initialHp)
    {
        // Declarative Property
        CurrentHp = new ReactiveProperty<long>(initialHp);
        IsDead = CurrentHp.Select(x => x <= 0).ToReactiveProperty();
    }
}

// ---
// onclick, HP decrement
MyButton.OnClickAsObservable().Subscribe(_ => enemy.CurrentHp.Value -= 99);
// subscribe from notification model.
enemy.CurrentHp.SubscribeToText(MyText);
enemy.IsDead.Where(isDead => isDead == true)
    .Subscribe(_ =>
    {
        MyButton.interactable = false;
    });
你可以用UnityEvent.AsObservable将ReactiveProperties, ReactiveCollections 和 observables 组合起来。 所有UI组件都是observable的。
一般来说ReactiveProperties不是可序列化的或者说在Unity编辑器的Inspector面板中看不到,但是UniRx提供了特殊的子类来实现这个功能。包括Int/LongReactiveProperty, Float/DoubleReactiveProperty, StringReactiveProperty, BoolReactiveProperty,还有更多参见:InspectableReactiveProperty.cs(https://github.com/neuecc/UniRx/blob/master/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/InspectableReactiveProperty.cs))。都可以在Inspector中编辑。对于自定义的枚举ReactiveProperty,写一个可检视的ReactiveProperty[T]也很容易。
如果你需要[Multiline] 或者 [Range] 添加到ReactiveProperty上,你可以使用 MultilineReactivePropertyAttribute 和 RangeReactivePropertyAttribute 替换 Multiline 和 Range。
这些InpsectableReactiveProperties可以在inspector面板显示,并且当他们的值发生变化时发出通知,甚至在编辑器里变化时也可以。
这个功能是实现在 InspectorDisplayDrawer(https://github.com/neuecc/UniRx/blob/master/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/InspectorDisplayDrawer.cs)。你可以通过继承这个类实现你自定义的ReactiveProperties在inspector面板的绘制:
public enum Fruit
{
    Apple, Grape
}

[Serializable]
public class FruitReactiveProperty : ReactiveProperty<Fruit>
{
    public FruitReactiveProperty()
    {
    }

    public FruitReactiveProperty(Fruit initialValue)
        :base(initialValue)
    {
    }
}

[UnityEditor.CustomPropertyDrawer(typeof(FruitReactiveProperty))]
[UnityEditor.CustomPropertyDrawer(typeof(YourSpecializedReactiveProperty2))] // and others...
public class ExtendInspectorDisplayDrawer : InspectorDisplayDrawer
{
}
如果ReactiveProperty的值只在stream中更新,你可以用 ReadOnlyReactiveProperty 让这个属性只读。
public class Person
{
    public ReactiveProperty<string> GivenName { get; private set; }
    public ReactiveProperty<string> FamilyName { get; private set; }
    public ReadOnlyReactiveProperty<string> FullName { get; private set; }

    public Person(string givenName, string familyName)
    {
        GivenName = new ReactiveProperty<string>(givenName);
        FamilyName = new ReactiveProperty<string>(familyName);
        // If change the givenName or familyName, notify with fullName!
        FullName = GivenName.CombineLatest(FamilyName, (x, y) => x + " " + y).ToReadOnlyReactiveProperty();
    }
}
MVP设计模式 Model-View-(Reactive)Presenter Pattern
用UniRx可以实现MVP(MVRP)设计模式。
为什么应该用MVP模式而不是MVVM模式?Unity没有提供UI绑定机制,创建一个绑定层过于复杂并且会对性能造成影响。 尽管如此,视图还是需要更新。Presenters层知道view的组件并且能更新它们。虽然没有真的绑定,但Observables可以通知订阅者,功能上也差不多。这种模式叫做Reactive Presenter:
// Presenter for scene(canvas) root.
public class ReactivePresenter : MonoBehaviour
{
    // Presenter is aware of its View (binded in the inspector)
    public Button MyButton;
    public Toggle MyToggle;

    // State-Change-Events from Model by ReactiveProperty
    Enemy enemy = new Enemy(1000);

    void Start()
    {
        // Rx supplies user events from Views and Models in a reactive manner
        MyButton.OnClickAsObservable().Subscribe(_ => enemy.CurrentHp.Value -= 99);
        MyToggle.OnValueChangedAsObservable().SubscribeToInteractable(MyButton);

        // Models notify Presenters via Rx, and Presenters update their views
        enemy.CurrentHp.SubscribeToText(MyText);
        enemy.IsDead.Where(isDead => isDead == true)
            .Subscribe(_ =>
            {
                MyToggle.interactable = MyButton.interactable = false;
            });
    }
}

// The Model. All property notify when their values change
public class Enemy
{
    public ReactiveProperty<long> CurrentHp { get; private set; }

    public ReactiveProperty<bool> IsDead { get; private set; }

    public Enemy(int initialHp)
    {
        // Declarative Property
        CurrentHp = new ReactiveProperty<long>(initialHp);
        IsDead = CurrentHp.Select(x => x <= 0).ToReactiveProperty();
    }
}
视图层是一个场景scene,是Unity的hierachy定义的。展示层在Unity初始化时将视图层绑定。XxxAsObservable方法可以很容易的创建事件信号signals,没有任何开销。SubscribeToText and SubscribeToInteractable 都是简洁的类似绑定的辅助函数。虽然这些工具很简单,但是非常有用。在Unity中使用很平滑,性能很好,而且让你的代码更简洁。
V -> RP -> M -> RP -> V 完全用响应式的方式连接。UniRx提供了所有的适配方法和类,不过其他的MVVM(or MV*)框架也可以使用。UniRx/ReactiveProperty只是一个简单的工具包。
GUI编程也可以从ObservableTriggers获益良多。ObservableTriggers将Unity事件转为Observables,所以MV(R)P模式可以用它们来组成。例如 ObservableEventTrigger 将 uGUI 事件转为 Observable:
var eventTrigger = this.gameObject.AddComponent<ObservableEventTrigger>();
eventTrigger.OnBeginDragAsObservable()
    .SelectMany(_ => eventTrigger.OnDragAsObservable(), (start, current) => UniRx.Tuple.Create(start, current))
    .TakeUntil(eventTrigger.OnEndDragAsObservable())
    .RepeatUntilDestroy(this)
    .Subscribe(x => Debug.Log(x));
未完结,请关注“洪流学堂”公众号后续连载

回复

使用道具 举报

7日久生情
2533/5000
排名
3334
昨日变化
2

2

主题

1780

帖子

2533

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
241666
好友
0
蛮牛币
10193
威望
0
注册时间
2017-9-6
在线时间
360 小时
最后登录
2018-5-23
发表于 2018-2-8 08:53:33 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

关闭

站长推荐 上一条 /1 下一条

快速回复 返回顶部 返回列表