MVVM框架在unity开发中的使用
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后,它提供了一些使用示例:
在这个示例中,有两个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
如有错误,欢迎指正!