【Unity】UI开发之MVC模式

http://blog.csdn.net/jjiss318/article/details/44220081html

原文:http://engineering.socialpoint.es/MVC-pattern-unity3d-ui.htmlgit

动机github

        和游戏开发的其余模块相似,UI通常须要经过屡次迭×××发,直到用户体验近似OK。另外相当重要的是, 咱们想尽快加速迭代的过程。使用MVC模式来进行设计,已经被业界证实了是能够解耦屏幕上的显示,如何控制用户的输入对显示的改变,以及如何根据应用的状态进行改变。MVC模式提供了如下好处:安全

(1) 能够修改UI的外观,而不用修改一行代码ide

(2) 在不一样的组件里面能够共享同一套逻辑代码,用来建立复杂的视图;布局

(3) 能够用很小的代价来改变UI的实现,好比正在使用NGUI , 但未来会切换成 UGUI动画

 

代码示例ui

这里提供了一个MVC在使用NGUI开发的例子,须要注意的是,咱们不能在代码中提供NGUI库,毕竟它是收费的,因此想运行起来的话,须要本身导入NGUI库。接下来的内容都会以这个例子的代码做为基准,因此能够在阅读时能够先把代码下载下来。spa


整体概述.net

这是一张简图,从宏观的角度描述了代码中MVC模式的不一样部分。

        

Model

  传统的MVC中的Model,咱们都很熟悉:

        (1) 不保存任何View的数据或者View的状态

        (2) 只能被Controller或者其余Model访问

        (3) 会触发事件来通知外部系统进行处理和变动

这里的Model是使用Plain Old C# Objects(POCOs)来实现的,就是不依赖于任何外部库的C#代码。这是例子中对于PlayerModel的连接地址,它表示了Player的数据,包括HitPoints,XP和level,使用两个属性来进行访问。咱们能够增长XP点,而Model则会raise一个XPGained事件。当得到升级的经验时,Level属性会更新,并raise一个LevelUp事件。


 View

概念上的view通常就是在屏幕上渲染的东西。View的职责包括:

   (1) 处理用户绘制元素的reference,包括纹理,特效等

(2) 播放动画

(3) 布局

(4) 接受用户输入

在代码这个特定例子中,View的实现使用了NGUI,因此它只是Unity工程中的一个Prefab。但这个实现细节须要解耦。想了解更多学术上的知识的话,还能够看下Passive ViewView不知道工程中其余部分的任何事情,不管是数据仍是逻辑。这样其余的代码必选显式地告诉View显式什么,播放什么动画等等。


Controller

Controller是链接Model和View的桥梁。它会保存View的状态,而且根据外部事件来更新View的状态:

(1) 持有View所须要的应用状态

(2) 控制View的流程

(3) 根据状态show/hides/activates/deactivates/updates View或者View的某些部分。如controller可临时将***按钮Distable掉,由于此时***处于冷却状态,冷却状态一过,controller会re-enable这个按钮。

(4) load/Instantiate须要的assets,好比显示particles, 动态改变sprites等

   (5) 处理用户在View中触发的事件,好比用户按下了一个按钮;处理Model触发的事件,好比player得到了      XP并触发了升级,因此controller就更新了View中的Level Number

这就是MVC模式中定义的三个基本元素。但在这个例子中,咱们加入了另一个中间层来进一步解耦NGUI的View实现,称之为:


ViewPresenter

一个ViewPresenter位于View和Controller之间,做为一个接口存在,暴露了对于一个View来讲是普适的操做集合,不管View自己是基于什么库来实现的(NGUI,UGUI等)

好比一个游戏中的按钮,通常来讲都有如下功能集:

(1) 设置按钮label中的文字

(2) 修改按钮的背景图

(3) enable/disable用户输入

(4) 当用户点击按钮时,进行Notify

这些都是与UI具体实现无关的操做,在任何一个UI toolkit中都能找到这些操做。ViewPresenter实现为一个MonoBehaviour被Attach到NGUI View的Prefab上,因此能够在Controller中经过GameObject.GetComponent来获得它来进行功能调用。因为ViewPresenter是游戏代码和UI代码的桥梁,因此它不能彻底独立于屏幕渲染的底层实现。在这个例子中,ViewPresenter须要持有NGUI Widgets(好比UIButton,UILabel等)的引用,用来于它们进行交互。其实ViewPresenter其实是一个适配器模式(Adapter pattern)的实现:  咱们额外建立了定制的接口来对应用进行访问。这些引用必须在Inspector或者其余代码中进行设置。

幸运的是,这个设置过程能够部分自动化,能够参看ViewPresenter.AutoPopulateDeclaredVidgets()虽然ViewPresenter和某个特定的UI系统有耦合,但使用建立用于Controller的接口获得了一个好处:若是须要更换GUI库,只须要修改修改ViewPresenter的实现,而不须要修改ViewPresenter的接口以及controller的任何逻辑。

之因此将其称之为ViewPresenter是由于它与Model-View-presenter模式中的Presenter有些相似,只不过Presenter能够访问Model,但ViewPresenter不能够。

ViewPreseter能够持有一些player标识的状态,好比View存储了不一样的颜色来提示heath point( 以健康度做为基准来提示满血,充裕,虚弱之类的),这些值能够暴露出来做为公共属性,运行在运行时经过inspector来进行实时修改。但不管如何,ViewPresenter不能持有应用程序逻辑的任何状态,而逻辑由controller管理,ViewPresenter根本不该该知道什么样的heath level是表示low。

好比,若是扩展PlayerController来处理Hit points,能够增长一个方法来改变label的颜色,当处于low health的时候:

public class PlayerController{   // ...
    void UpdateHitPointsUI()
    {        if (Player.HasLowHitPoints)
        {
            HitPointsViewLabel.ShowLowHealthColor();
        }        else
        {
            HitPointsViewLabel.ShowNormalHealthColor();
        }
    }}

 

           若是你想建立一个很是特定或者复杂的UI,并在工程中进行重用的话,这个方法可能有些过分设计了。在这个Sample代码中,咱们很轻松地进行了如此处理:controller只须要修改ViewPresenter中的一个UnityEngine.Color类型的属性。

Handling UI events

NGUI提供了一个设计良好的时间系统,在任何定义了某事件hander的MonoBehaviour中均可以触发这个事件。这样就解耦了触发事件的MonoBehaviour和处理这个事件的MonoBehaviour。而后,强大的功能增长告终构混乱的机会,由于可使用任何MonoBehaviour做为事件的handler,很随手的就把scene中的某个monoBehaviour拖放到inspector的handler上了,这样当建立一个包含了若干个控件的复杂View时,就容易获得一个难以track的依赖,这个依赖图的复杂度能够很是快速地增加。

为了防止代码中混乱的蔓延,咱们遵照了一个很是容易的原则:全部的View的UI事件只能被attatch到这个View的ViewPresenter来进行处理。ViewPresenter会捕获NGUI的事件,并Raise一个 .net的Event做为回应。其余的代码只订阅那个.net时间。这么作是由于须要将UI事件的具体实现与NGUI解耦,由于这样作咱们使用代码中的事件,而不是inspector的事件。在咱们的观点中,这是一个更安全的方法:能够很容易地在IDE中搜索处理这个Event的代码;并且更加类型安全:(若是你删除了一个处理这个Event的MonoBehavior,你只会发现控件在Play Mode中中止工做了), 能够容许设置Event上的各个参数。固然咱们须要在View Presenter中封装NGUI的Event,但咱们的使之自动化:看看这个代码ButtonViewPresenter.wireUIEvents()

 

建立复杂的View

如今已经有一些模块能够支持建造了,经过组合来建立一些更复杂的View变得容易:由若干个UI prefab来组合成一个新的View, 并为这个组合出来的view建立一个新的ViewPresenter, 将子View中的ViewPresenter暴露出来,这样就能够在controller中进行访问了。



这个组合出来的新ViewPresenter,能够在这里找到。

结语

欢迎告知使用这种方法的感觉,并欢迎告知大家在游戏UI开发中的独特方法,欢迎各类反馈。

另外,若是不使用NGUI,而是使用Unity3D的GUI,那么实现ViewPresenter的OnGUI方法为Unity3D自带的GUI API将会是一个不错的体验。

相关文章
相关标签/搜索