1、什么是MVVM

借用一下百度百科上对MVVM的介绍,MVVM是Model-View-ViewModel的简写,它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
 

2、MVVM在unity开发中的应用

MVVM框架应用面十分广泛,通常在前端开发中应用很广,最近看到周围同事在开发WPF时用到了这个框架,抱着好奇的态度来学习一下这个框架,MVVM框架在unity开发中同样适用,在unity中,将每个UI抽象成一个个View,通常我们为每一个UI面板定义一个View,View中包含了该面板中涉及到的UI元素,比如一个Text,一个Button;每一个View都有独立的ViewModel来管理,ViewModel中提供必要的属性和方法来控制View, 而Model只是单纯的定义一个数据模型。前两天在github上发现了这个叫uMVVM的框架,拿来试用了一下,下面分析一下该框架对MVVM模式的设计。
 

3、uMVVM的设计与实现

下载uMVVM后,它提供了一些使用示例:

MVVM框架在unity开发中的使用-LMLPHP

在这个示例中,有两个panel,就定义了两个view,每个view中定义该界面的元素,比如SetupView:
public class SetupView:UnityGuiView<SetupViewModel>
    {

        public InputField nameInputField;
        public Text nameMessageText;

        public InputField jobInputField;
        public Text jobMessageText;

        public InputField atkInputField;
        public Text atkMessageText;

        public Slider successRateSlider;
        public Text successRateMessageText;

        public Toggle joinToggle;
        public Button joinInButton;
        public Button waitButton;

        public SetupViewModel ViewModel { get { return (SetupViewModel)BindingContext; } }

        }
可以看到,View中需要指定对应的ViewModel来管理该View,ViewModel中定义的属性需要具备当数据改变时通知订阅者的功能,因此uMVVM对这种属性进行了一层封装,具体设计如下:
public class BindableProperty<T>
    {
        public delegate void ValueChangedHandler(T oldValue, T newValue);

        public ValueChangedHandler OnValueChanged;

        private T _value;
        public T Value
        {
            get
            {
                return _value;
            }
            set
            {
                if (!Equals(_value, value))
                {
                    T old = _value;
                    _value = value;
                    ValueChanged(old, _value);
                }
            }
        }

        private void ValueChanged(T oldValue, T newValue)
        {
            if (OnValueChanged != null)
            {
                OnValueChanged(oldValue, newValue);
            }
        }

        public override string ToString()
        {
            return (Value != null ? Value.ToString() : "null");
        }
    }
可以看到,BindableProperty类中维护一个T类型数据,当T发生变化时,可以通知到订阅者,有了这种属性之后,那么ViewModel就是这样的了:
public class SetupViewModel:ViewModelBase
    {
        public readonly BindableProperty<string> Name = new BindableProperty<string>();
        public readonly BindableProperty<string> Job=new BindableProperty<string>();
        public readonly BindableProperty<int> ATK = new BindableProperty<int>();
        public readonly BindableProperty<float> SuccessRate=new BindableProperty<float>();
        public readonly BindableProperty<State> State=new BindableProperty<State>();
}
uMVVM的设计中,每个View都继承UnityGuiView这个泛型类,UnityGuiView大概的内容是这样:
public abstract class UnityGuiView<T>:MonoBehaviour,IView<T> where T:ViewModelBase
    {
        private bool _isInitialized;
        public bool destroyOnHide;
        protected readonly PropertyBinder<T> Binder=new PropertyBinder<T>();
        public readonly BindableProperty<T> ViewModelProperty = new BindableProperty<T>();

        public T BindingContext
        {
            get { return ViewModelProperty.Value; }
            set
            {
                if (!_isInitialized)
                {
                    OnInitialize();
                    _isInitialized = true;
                }
                //触发OnValueChanged事件
                ViewModelProperty.Value = value;
            }
        }

        /// <summary>
        /// 初始化View,当BindingContext改变时执行
        /// </summary>
        protected virtual void OnInitialize()
        {
            //无所ViewModel的Value怎样变化,只对OnValueChanged事件监听(绑定)一次
            ViewModelProperty.OnValueChanged += OnBindingContextChanged;
        }

        /// <summary>
        /// 当gameObject将被销毁时,这个方法被调用
        /// </summary>
        public virtual void OnDestroy()
        {
            if (BindingContext.IsRevealed)
            {
                Hide(true);
            }
            BindingContext.OnDestory();
            BindingContext = null;
            ViewModelProperty.OnValueChanged = null;
        }

        /// <summary>
        /// 绑定的上下文发生改变时的响应方法
        /// 利用反射+=/-=OnValuePropertyChanged
        /// </summary>
        public virtual void OnBindingContextChanged(T oldValue, T newValue)
        {
            Binder.Unbind(oldValue);
            Binder.Bind(newValue);
        }
    }
UnityGuiView中有一个BindingContext的属性, 当使用框架时,需要给View的BindingContext指定对应的ViewModel:
  public class Install:MonoBehaviour
    {
        // Use this for initialization
        public SetupView setupView;
        public TestView testView;
        void Start()
        {
            //绑定上下文
            setupView.BindingContext=new SetupViewModel();
            testView.BindingContext=new TestViewModel();
        }
    }
在View中,就订阅model数据改变的消息,并定义相应的响应函数:
 public class SetupView:UnityGuiView<SetupViewModel>
    {
        //......省略ui元素的定义

        protected override void OnInitialize()
        {
            base.OnInitialize();
            Binder.Add<string>("Name", OnNamePropertyValueChanged);
            Binder.Add<string>("Job",OnJobPropertyValueChanged);
            Binder.Add<int>("ATK",OnATKPropertyValueChanged);
            Binder.Add<float>("SuccessRate",OnSuccessRatePropertyValueChanged);
            Binder.Add<State>("State",OnStatePropertyValueChanged);
        }


        private void OnSuccessRatePropertyValueChanged(float oldValue, float newValue)
        {
            successRateMessageText.text = newValue.ToString("F2");
        }

        private void OnATKPropertyValueChanged(int oldValue, int newValue)
        {
            atkMessageText.text = newValue.ToString();
        }

        private void OnJobPropertyValueChanged(string oldValue, string newValue)
        {
            jobMessageText.text = newValue.ToString();
        }

        private void OnNamePropertyValueChanged(string oldValue, string newValue)
        {
            nameMessageText.text = newValue.ToString();
        }
        private void OnStatePropertyValueChanged(State oldValue, State newValue)
        {
           //dosomething
        }

最后看一下其中一个model的定义:

 public class Combatant
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Job { get; set; }
        public float SuccessRate { get; set; }
        public State State { get; set; }
    }
    public enum State
    {
        JoinIn,
        Wait
    }

4、总结

本文只大概写了一下uMVVM框架的一些设计和使用方法,不全面,如果感兴趣,可以自行阅读源码,github地址为https://github.com/MEyes/uMVVM
 
如有错误,欢迎指正!
01-22 18:19