MVVM框架在unity开发中的使用

摘要:
publicSetupViewModelViewModel{get{return(SetupViewModel)BindingContext;publicTValue{get{return_value;_value=value;_value);value.ToString():“null”);Name=newBindableProperty<

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开发中的使用第1张

在这个示例中,有两个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
 
如有错误,欢迎指正!

免责声明:文章转载自《MVVM框架在unity开发中的使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇stimulsoft Report报表使用笔记Go语言基础之单元测试下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【转】TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端

【转】TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端 目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP部分的使用 框架源码结构 补充说明 源码地址 说明 之前有好几篇博客在讲TCP/UDP通信方面的内容,也有做过一些Demo(包括整理出来的、可供学习使用的...

ThinkPHP部署

TP框架中使用单一入口文件作为调用框架中的方法属性处理业务逻辑,框架中的配置文件,分为3级框架级,应用级,分组级,加载顺序,框架级->应用级->分组级,后面的会覆盖前面的。 TP中的URL访问模式(路由模式): 访问模式也就是不同格式的url请求,tp框架中分4中访问模式 1.普通模式,http://域名/入口文件?m=分组名&c=控制...

TP3初步了解

什么是框架:       所谓的框架,它就是某个web应用程序的半成品,就是一组组件(分页类、验证码类、文件上传类、DB类、Image类等。。。),利用这些组件完成自己的web应用系统。 用框架的好处:       用框架能节省开发的时间,因为框架已经把各个类(比如分页、验证码)封装好,用的时候可以直接调用,而且也利于团队开发,可以使我们的主要精力用于业务...

NVIDIA深度学习Tensor Core性能解析(上)

NVIDIA深度学习Tensor Core性能解析(上) 本篇将通过多项测试来考验Volta架构,利用各种深度学习框架来了解Tensor Core的性能。 很多时候,深度学习这样的新领域会让人难以理解。从框架到模型,再到API和库,AI硬件的许多部分都是高度定制化的,因而被行业接受的公开基准测试工具很少也就不足为奇。随着ImageNet和一些衍生模型(Al...

Django REST framework 中文文档

Django REST framework介绍 现在前后端分离的架构设计越来越流行,业界甚至出现了API优先的趋势。 显然API开发已经成为后端程序员的必备技能了,那作为Python程序员特别是把Django作为自己主要的开发框架的程序员,我十分推荐Django REST framework(DRF)这个API框架。   Django REST frame...

tp框架事务处理

当我们需要同时对多个表进行操作的时候就有必要进行事务处理,首先你的数据库和数据表必须满足事务处理,即表引擎为InnoDB 下面为一个demo //事务:表必须是innodb//删除主表$mod1 = M('User');//删除详情表$mod2 = M('Userdetail');$mod1->startTrans();//开启事务if(!$mod...