提到游戏的UI开发,几乎没人没听说过MVC吧?MVC最早是为软件架构设计的框架,这里不打算讲MVC的演变过程,需要的了解的可以参考大神的文章,传送门:
你对MVC、MVP、MVVM 三种组合模式分别有什么样的理解?
高赞回答。
另外,我也喜欢第二个回答的总结:
M-V- X 本质都是一样的 重点还是在于M-V 的桥梁,要靠X来牵线。
在相同技术栈下 能够实现的各种 X都可以是大同小异的。
在不同技术栈下 相同的X可能实现都大相径庭,仅有非常抽象的流程类似。
所以UI部分的开篇我们不讲代码,只讲概念。
1.1 M-V-C
看下百度百科的定义:
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
架构的兴起有它的时代和背景意义,那么随着时代的变迁,工具和大环境甚至是目标设备的变化,框架都是需要逐步调整的。
MVC出现的初衷是源自于与操作系统和软件的日益复杂化。MFC或者VB那种界面和逻辑揉在在一起的模式已经远远不能满足于大型复杂的功能系统开发。
然后随着软件行业的日益成熟,团队的职能分工也越来越明确,那么如何揉在一起的任务拆离出来,让不同职能的人专注于自己的领域和设计也是一个重要的方面。
基于很多的原因,软件UI框架开始分化为M-V-C的模式。M层专注于数据处理, V层专注实现的专注于表现,柱状图,饼状图、表格按你意愿。
一个典型的MVC的框架可以这么表示:
话说真的爱死 Unity的Animator编辑器了,好方便。
可以看到这是一个局部生态的自给自足。考虑了大部分的交互和变更情况,并通过规定每个部分的职能来满足功能需求。
但是MVC并不是完美的,严格来说它是只是一种指导性的框架,是综合了大部分的软件需求和时代发展结合得出的相对较优的方案。这种模式仍然存在它的缺陷:
1.局部生态,数据(M层)封闭,如果多个模块需要同一个数据块,数据之间的互通和重用性都非常的低效。
2.局部耦合,虽然在大的环境下实现了局部封闭,但是局部内的各个层之间的逻辑耦合还是很深。
想象一下,如果把上面的局部展示放大到游戏开发中的话会是什么样?
在这个情境下,MVC的框架就不能很好的应对了。游戏数据很多时候是互相依赖,并不能完全封闭。比如角色的属性展示需要用到背包里的装备,背包需要显示货币,货币可能受某些角色属性影响,任务依赖角色等级,同时奖励货币和道具、装备等等。
那么这种情况下,MVC要么需要将M写的非常复杂,每遇到一个新的需求开出一个新的接口,提供数据插叙或者变更,要么写一个通用的M管理器,用来统一中转数据。
在游戏开发这种需求多变,并且数据嘈杂的背景下,无论哪种都不能很好的应对需求,并且开发人员会受到无尽的折磨,在遵循架构和实现需求的之间来回纠结,摩擦,最终写的不伦不类,BUG还超多。
1.2 M-V-P
有需求就要有变化,当一个需求只是个别现象的时候,你可以特殊处理、特殊对待。但需求大批量出现的时候,就得重新审视现在的实现是不是需要重构或者升级了。
来看看谷歌的MVP模式:
https://github.com/googlesamples/android-architecture/tree/todo-mvp/github.com
MVP即Model-View-Presenter。
Model的工作就是完成对数据的操纵,数据的获取、存储、数据状态变化都是model层的任务,如网络请求,持久化数据增删改查等任务
View只处理视图相关,不做任何逻辑处理。
Presenter作为桥梁,处理所有二者之间的中转。
在这个模式下,M和V的连接被完全切断了,以前C层只是负责一些简单的转发和处理,现在P的任务变的更重,除了桥梁的作用之外,还需要做初步甚至高级的逻辑处理来处理M-V或者V-M的交流过程。
居然P的任务变重了,那么相对来说,P也会变得更加臃肿和难以维护,但是好处是将M和V彻底解耦,不管哪一方的实现方式发生变化,只要最终和P同步的数据不变另一方都不需要关心和修改。
另外V的逻辑功能一部分移入P之后,V可以更加专注的处理自身的表现,同时因为对接是通过接口实现的,所以满足接口的各种测试或者模拟都能够得以使用。
另外这里每一个V都对应一个P,写法非常复杂,软件复杂的时候,类和文件贼多。并且它仍然没有解决M复用的问题。
1.3 M-V-VM
MVVM是Model-View-ViewModel的简写。
从示意图上最直观的感受是两个:
1.使用ViewModel替代了Presenter。
2.原本P和V一对一的关系现在变为VM-V一对多的关系。
这解决了什么问题呢?
1.VM在一定程度上能够重用,就表示M层在一定程度上也可以复用了。
2.VM一对多的关系,表示在类和文件的数量和管理上要减轻很多。
VM是通过DataBinding的技术实现V和M之间的关系映射。抛弃了P层的手动关系接口和维护。当然每种技术都有其存在的意义和解决的问题。至于选取什么样的方案去解决问题,就要看项目自己的需求更符合哪一类的设计。如果都没有那就需要自己去实现变种或者是新的设计,当然也可以修改需求的啦。
1.4 游戏开发应该使用什么?
注意,以上的三种方法都是来源于软件,都是来源于软件,都是来源于软件 。游戏开发中也都是套用这些概念来进行UI的开发设计的。至于实现过程依据引擎和平台的差别肯定是不一样的,那么我们使用什么样的方式呢?
我的UI框架设计里,没有使用DataBinding技术,要仔细来看的话,它可能不属于以上的任意一种。那么我给它命名为MVE!即Model-View-Event。
MVC重点逻辑在V,MVP重点逻辑在P,MVVM,重点逻辑还是在V和VM,V负责UI逻辑部分,VM负责数据绑定部分。
那么MVE可以认为是MVC的一个变种,但是V会和数据中心共享。
话不多说看图:
作为网络游戏来说,数据应该来自两个部分,一部分是策划编辑的数据,一部分是通过服务器下发的数据。界面通过注册事件来订阅指定的事件类型,数据中心通过和服务器之间的交互来获得或者改变数据,并根据需求推送指定的Event,当界面关心的事件发生的时候,它有两个可能,一部分是动态变化的局部更新,可以通过事件带下来,另外一种是整体数据需要去数据中心获取。
举个例子,当我打开背包界面的时候,我需要知道所有的背包数据,因此V和M直接交互,拿到背包的所有道具,并显示自己。然后我吃掉了一个经验丹,这时候服务器会告诉客户端删除一个物品,这个时候数据层会通过事件触发,背包监听了这个事件,然后查找对应的道具ID,删除,然后对背包道具进行局部重新排列刷新。
假设我们还有一个界面叫做一键升级,可以选择吃掉背包内不同种类的经验丹,那么它打开的时候同样找数据中心拿取道具这部分相关的数据做显示,如果这个时候在背包内吃掉一个,那么同样监听了这条消息的一键升级界面也要重新显示数量。
所以V和M的关系只是查询,并不会改变数据,数据的变化只能来自于服务器的协议驱动。(当然一些客户端自定义的用于辅助的数据,比如排序列表,计时器等VIEW层自己变化就好。)
另外,其实开篇我也有提到,这里再说一下:
M-V-X 本质都是一样的 重点还是在于M-V的桥梁,要靠X来牵线。
在相同技术栈下 能够实现的各种 X都可以是大同小异的。
在不同技术栈下 相同的X可能实现都大相径庭,仅有非常抽象的流程类似。
再者,不管是框架还是设计模式都是为了解决实际问题的,不需要也不应该为了设计而过度设计,但是也不能完全没有章法,胡乱定义。
一个稳定的,有序的能满足项目需求的实现就是好的实现。切莫过度纠结设计模式和框架结构,但是也不能轻视这部分,够用就好,够用就好。