千锋教育-做有情怀、有良心、有品质的职业教育机构
前言
xlua-framework
xlua-framework-unity2018
我们前面说过xlua-framework这个框架,是一个纯lua的一个框架,也就是说我们所有的逻辑都可以在lua中实现,只有资源模块和网络模块是c#原生写的,但是也在lua中给我们包装的工具类,让我们更方便的使用。在框架的纯lua代码中,不得不说,ui框架是我最喜欢的,一套基于mvc的一套ui框架。
MVC框架
说到MVC框架,很多人可能会说,mvc根本就不适合游戏,有这种想法的人,一定是个编程新手,我们在介绍UI框架前,先来介绍下MVC。
首先,mvc,是一种编程思想或者说框架思想,并不是一个设计模式,而这种思想的核心是分层,也就是说,我们要把ui相关的界面显示和ui数据存储以及ui控制逻辑分开不同的文件来写,为什么要分层呢?这和Web的mvc思想是一致的,都是为了降低我们类的复杂度,方便我们阅读和扩展,如下图所示:
比如说,一个复杂的UI页面类,有2000多行代码。如果我们代码结构写的不好,会发现这个类很难维护,也很难扩展,甚至连阅读都很难,接下来我们针对上面这个UI做一个小优化:我们把UI控件显示相关的函数,独立到一个View类中,数据相关的放到Model类中,控制输入相关的放到Ctrl类中。接下来我们这个页面就很容易扩展和阅读了,比如我们只是修改ui显示相关的,那我们之控制View类就行了,大概也就4-5百行左右的代码,很好维护,同理,如果是想修改用户输入控制的方法,直接修改Ctrl类就行了,如果只是处理UI显示的数据相关,直接修改Model类中对数据整理相关的函数就可以了。
MVC的变体
1.MVC(Model-View-Controller)
MVC是比较直观的架构模式,用户操作->View(负责接收用户的输入操作)->Controller(业务逻辑处理)->Model(数据持久化)->View(将结果反馈给View)。
MVC使用非常广泛,比如JavaEE中的SSH框架(Struts/Spring/Hibernate),Struts(View, STL)-Spring(Controller, Ioc、Spring MVC)-Hibernate(Model, ORM)以及ASP.NET中的ASP.NET MVC框架,xxx.cshtml-xxxcontroller-xxxmodel。
2.MVP(Model-View-Presenter)
MVP是把MVC中的Controller换成了Presenter(呈现),目的就是为了完全切断View跟Model之间的联系,由Presenter充当桥梁,做到View-Model之间通信的完全隔离。
.NET程序员熟知的ASP.NET webform、winform基于事件驱动的开发技术就是使用的MVP模式。控件组成的页面充当View,实体数据库操作充当Model,而View和Model之间的控件数据绑定操作则属于Presenter。控件事件的处理可以通过自定义的IView接口实现,而View和IView都将对Presenter负责。
3.MVVM(Model-View-ViewModel)
如果说MVP是对MVC的进一步改进,那么MVVM则是思想的完全变革。它是将“数据模型数据双向绑定”的思想作为核心,因此在View和Model之间没有联系,通过ViewModel进行交互,而且Model和ViewModel之间的交互是双向的,因此视图的数据的变化会同时修改数据源,而数据源数据的变化也会立即反应到View上。
这方面典型的应用有.NET的WPF,js框架Knockout、AngularJS等。
xluaframework的MVC结构
当请求UI管理器打开窗口时,UI管理器会根据UI配置找到MVC各层脚本文件并实例化对象,生成一个UIWindow,一个window包含了View,Model,Ctrl三个组件,但是也不是一定modle和ctrl都要有,如果你的页面非常简单,可以没有Model和Ctrl,或者只有Model没有Ctrl,只有Ctrl没有Model都可以。但是标准的页面我们是需要MVC相互配合来实现的。
View层:负责界面所有UI刷新操作,只和展示相关的数据放在这,只有操作相关数据放Model去
注意:
1、被动刷新:所有界面刷新通过消息驱动—除了打开界面时的刷新
2、对Model层可读,不可写—调试模式下强制
3、所有写数据、游戏控制操作、网络相关操作全部放Ctrl层
4、Ctrl层不依赖View层,但是依赖Model层
5、任何情况下不要在游戏逻辑代码操作界面刷新—除了打开、关闭界面
Model层:负责界面相关数据,同时负责消息定制和分发
注意:
1、数据大体分为两类:游戏逻辑数据、界面控制数据
2、游戏逻辑数据:从游戏数据中心取数据,这里不做为数据源,只做中转和必要处理(如排序),游戏中心数据改动以后在这里监听变化
3、界面控制数据:一般会影响到多个界面展示的控制数据,登陆界面显示当前服务器,当受到选服界面操作的影响
4、界面Model层对View层是只读不写的,一定不要在View层写Model
5、界面Model层不依赖Ctrl层和View层,意思是说拿掉这这两层代码,Model依旧能完好运行
6、界面Model层数据只影响UI,不影响游戏逻辑,游戏逻辑不能从Model层取数据,意思是没有界面,游戏依旧能跑
Ctrl层:负责发送网络请求(网络数据)、操作游戏逻辑、修改模型数据(本地数据)
注意:
1、UI控制层用于衔接Model层和View层,主要是用来修改数据,或者进行游戏逻辑控制
2、修改数据:界面操作相关数据直接写Model层、游戏逻辑相关数据写数据中心
3、游戏控制:发送网络请求、调用游戏控制逻辑函数
4、Ctrl层是无状态的,不能保存变量–调试模式下强制
UI脚本目录结构
分成两个部分:LuaScripts/Framework/UI为UI框架代码,LuaScripts/UI为UI逻辑代码。其中:
LuaScripts/Framework/UI/Base:基类
LuaScripts/Framework/UI/Component:UI组件
LuaScripts/Framework/UI/Message:UI消息定义
LuaScripts/Framework/UI/Util:UI工具类
LuaScripts/ UI/Config:窗口配置
LuaScripts/ UI/UIxxx:UI逻辑模块,其中:Controller、Model、View对应控制、模型、视图脚本;而UIxxxConfig为模块配置脚本
UI模块添加流程
拼预设:如AssetsPackage/ UI/ Prefabs/ View/ UILogin.prefab
UI名配置:LuaScripts/ UI/Config/ UIWindowNames中添加
导入配置:LuaScripts/ UI/Config/ UIConfig中添加
脚本配置:LuaScripts/ UI/UIxxx/UIxxxConfig中配置,需要注意的是:Model、Ctrl、View层如果没有实现脚本,则直接填写nil;PrefabPath为相对于AssetsPackage目录的路径,需要带文件名后缀
在原框架xluaframework中,没有带ui的的自动创建工具,我们可以参考issues#39加一个工具加一个,xluaframework-unity2018的版本中已经带了这个工具,我们直接在菜单栏的Tools->MVC Tools打开UI创建面板
我们只需要根据自己的需要填上UI的名字和文件夹名字,然后选择是否需要Model和Ctrl,以及属于哪个层,最后点击CreateUILuaData按钮即可。
UI工作流程
当请求UI管理器打开窗口时,UI管理器会根据UI配置找到MVC各层脚本文件并实例化对象,生成一个UIWindow的数据结构标识一个窗口。立刻调度Model层OnEnable(首次打开先调用OnCreate),如果有传入参数,需要在此处保存下来;随后等待UI资源异步加载完毕,加载完毕后调度View层OnEnable(同样首次打开先调用OnCreate),View层随后实现类似Mono脚本的调度流程,View层可以随时读取Model层数据,但是写Model层数据需要经过Ctrl层;Ctrl层是无状态的,如果需要状态,写Model层就好。
脚本调度顺序:
OnCreate->OnEnable->Update->LateUpdate->OnDisable->OnDestroy
注意:Update相关函数添加上就会被调度,尽量不用,节省性能。界面逻辑书写流程和写Mono脚本大概一致。
数据流
A) 打开窗口时传入数据:OnEnable中传入Model层保存
B) Model层在OnEnable中从其他管理类或者数据类取数据并处理
C) View层可存放自身控制数据,直接读、间接写Model层数据
D) 模块与模块之间数据交换使用消息系统
注意:更多参考MVC各个实现脚本中的说明;技术细节直接参考源代码。
UI组件系统
为了实现类似Mono脚本的函数调度,做了一个组件系统,脚本目录在LuaScripts/Framework/UI/Component。组件大概分为两种:基础组件、容器组件。
基础组件是对Unity侧原生UI组件的封装,这层封装其实不要也行,只是封装后有些操作使用起来更加方便,比如图集图片切换;容器组件用于组合管理多个基础组件,并负责其中基础组件的函数调度。
基础组件:
UIButton,UICanvas,UIEffect,UIImage, UILayer,UIInput,UISlider,UIText,不做介绍,自己看源码
容器组件:
UITabGroup,UIButtonGroup,UIWrapGroup,不做介绍,自己看源码
自定义组件:
这里的自定义组件是指继承了UIBaseComponent的类,相当于子模块组件,对于十分复杂的UI界面,可以将模块独立的部分分离出来,写一个Component脚本(继承自UIBaseContainer),如主界面的小地图,如背包中的某个Item。这个Component类似Unity中的Mono脚本,挂上去就能用。
使用示例参考选服界面服务器列表中每个Item的刷新逻辑,那里把每个Item当做了一个Component去刷新。
View层脚本:
View层脚本是最上层的容器组件,管理视图中所有组件的调度。
特别需要注意的一点是:凡是Unity中的对象,在lua中进行判空时,不能直接判断是否等于nil,而必须使用IsNull方法来判断,这一点在XLua文档中也提到过。
UI层级管理
UI层级下的所有窗口根据OderInLayer的值来前后排布,为了避免层级混乱,这里进行统一管理,划分了一个子层级的概念,专供UI使用:
目前一共划分了6个子层级,第一个LuanchLayer只在启动界面使用;其余的子层级从上到下,展示时依次从背面到前面。
SceneLayer:用于场景UI
BackgroudLayer:背景UI
NormalLayer:普通一级、二级、三级UI
InfoLayer:信息UI
TipLayer:提示UI
TopLayer:顶层UI
归属于下一个子层级的UI窗口总是会盖住上一个子层级的UI窗口,不管打开顺序如何;而归属于同一个子层级的多个UI,其层级关系由打开的先后顺序决定,后打开的UI总是会盖住先打开的UI。
每两个相连子层级之间的OrderInLayer相间1000,而相同子层级的相连窗口OrderInLayer相间10,意思是说每个子层级最多可以同时打开100个窗口;而窗口内部还可以各自划分10个层级来使用,用于各个UI组件之间的层次关系(这些数值都可以根据具体情况调整配置)。在UGUI中,同属于同一个OrderInLayer的UI组件也可以排布先后,这种先后关系通过Hierarchy中的上下次序来决定,所以建议优先通过这种方式来排布先后,如果不能满足需求,再来使用不同的OrderInLayer来排布(会增加drawcall)。
有关窗口组件层级的示例代码参考登陆界面的相关示例,代码脚本路径:LuaScripts/ UI/UILogin/View/UILoginView.lua。这里展示了文字特效复杂的参差关系,如果需要扩展,可参考UICanvas组件代码。
总上所述
总的来说,这个mvc结构的UI框架是一个非常棒的ui结构,我非常的喜欢,也在我公司内部进行了推广,大家现在都已经熟练使用了这个结构。不过最开始大家也不是很习惯,使用过程中还是碰到过很多问题的,比如:
1.大家感觉页面拆开写很麻烦,虽然创建了Model和Ctrl但是里面都是空的:什么事总有第一次,永远不尝试,你就永远无法进步。等你写的多了,熟练了,才能慢慢掌握mvc的精髓。
2.大家感觉Ctrl和Model中不能直接访问View很不方便:建议大家把上面的MVC简介再好好看看,我们既要分层也要注意解耦,如果MVC之间来回调用,会让你的页面耦合度增加,维护起来更麻烦。
demo示例
下面我们展示一个非常简单的一个例子:
UILoginModel.lua
UILoginCtrl.lua
下一篇
unity如何接入puerts相关推荐