Unity3d中UI开发的MVC模式

Unity3d中UI开发的MVC模式 - 道心惟纯 - CSDN博客
关闭
var username = "jjiss318"; var _blogger = username; var blog_address = "http://blog.csdn.net/jjiss318"; var static_host = "http://static.blog.csdn.net"; var currentUserName = "";
    <div id="body">
        <div id="main">
            <div class="main">
                    <div class="ad_class">
window.quickReplyflag = true; var isBole = false; var fasrc="http://my.csdn.net/my/favorite/miniadd?t=Unity3d%e4%b8%adUI%e5%bc%80%e5%8f%91%e7%9a%84MVC%e6%a8%a1%e5%bc%8f&u=http://blog.csdn.net/jjiss318/article/details/44220081"

Unity3d中UI开发的MVC模式

    <div class="article_manage clearfix">
    <div class="article_r">
        <span class="link_postdate">2015-03-12 13:58</span>
        <span class="link_view" title="阅读次数">15914人阅读</span>
        <span class="link_comments" title="评论次数"> <a href="#comments" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_pinglun'])">评论</a>(3)</span>
        <span class="link_collect tracking-ad" data-mod="popu_171"> <a href="javascript:void(0);" onclick="javascript:collectArticle('Unity3d%e4%b8%adUI%e5%bc%80%e5%8f%91%e7%9a%84MVC%e6%a8%a1%e5%bc%8f','44220081');return false;" title="收藏">收藏</a></span>
         <span class="link_report"> <a href="#report" onclick="javascript:report(44220081,2);return false;" title="举报">举报</a></span>

    </div>
</div>    <style type="text/css">        
        .embody{
            padding:10px 10px 10px;
            margin:0 -20px;
            border-bottom:solid 1px #ededed;                
        }
        .embody_b{
            margin:0 ;
            padding:10px 0;
        }
        .embody .embody_t,.embody .embody_c{
            display: inline-block;
            margin-right:10px;
        }
        .embody_t{
            font-size: 12px;
            color:#999;
        }
        .embody_c{
            font-size: 12px;
        }
        .embody_c img,.embody_c em{
            display: inline-block;
            vertical-align: middle;               
        }
         .embody_c img{               
            width:30px;
            height:30px;
        }
        .embody_c em{
            margin: 0 20px 0 10px;
            color:#333;
            font-style: normal;
        }
</style>
<script  type="text/javascript">
    $(function () {
        try
        {
            var lib = eval("("+$("#lib").attr("value")+")");
            var html = "";
            if (lib.err == 0) {
                $.each(lib.data, function (i) {
                    var obj = lib.data[i];
                    //html += '<img src="' + obj.logo + '"/>' + obj.name + "&nbsp;&nbsp;";
                    html += ' <a href="' + obj.url + '" target="_blank">';
                    html += ' <img src="' + obj.logo + '">';
                    html += ' <em><b>' + obj.name + '</b></em>';
                    html += ' </a>';
                });
                if (html != "") {
                    setTimeout(function () {
                        $("#lib").html(html);                      
                        $("#embody").show();
                    }, 100);
                }
            }      
        } catch (err)
        { }

    });
</script>
  <div class="category clearfix">
    <div class="category_l">
       <img src="http://static.blog.csdn.net/images/category_icon.jpg">
        <span>分类:</span>
    </div>
    <div class="category_r">
                <label  onclick="GetCategoryArticles('1059978','jjiss318','top','44220081');">
                    <span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_fenlei']);">Unity3D<em>(29)</em></span>
                  <img class="arrow-down" src="http://static.blog.csdn.net/images/arrow_triangle _down.jpg" style="display:inline;">
                  <img class="arrow-up" src="http://static.blog.csdn.net/images/arrow_triangle_up.jpg" style="display:none;">
                    <div class="subItem">
                        <div class="subItem_t"><a  href="http://blog.csdn.net/jjiss318/article/category/1059978"  target="_blank">作者同类文章</a><i class="J_close">X</i></div>
                        <ul class="subItem_l" id="top_1059978">                            
                        </ul>
                    </div>
                </label>                    
    </div>
</div>

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

动机

        和游戏开发的其他模块类似,UI一般需要通过多次迭代开发,直到用户体验近似OK。另外至关重要的是, 我们想尽快加速迭代的过程。使用MVC模式来进行设计,已经被业界证明了是可以解耦屏幕上的显示,如何控制用户的输入对显示的改变,以及如何根据应用的状态进行改变。MVC模式提供了以下好处:

(1) 可以修改UI的外观,而不用修改一行代码

(2) 在不同的组件里面可以共享同一套逻辑代码,用来创建复杂的视图;

(3) 可以用很小的代价来改变UI的实现,比如正在使用NGUI , 但将来会切换成 UGUI


代码示例

这里提供了一个MVC在使用NGUI开发的例子,需要注意的是,我们不能在代码中提供NGUI库,毕竟它是收费的,所以想运行起来的话,需要自己导入NGUI库。接下来的内容都会以这个例子的代码作为基准,所以可以在阅读时可以先把代码下载下来。


总体概述

这是一张简图,从宏观的角度描述了代码中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 View。View不知道工程中其他部分的任何事情,无论是数据还是逻辑。这样其他的代码必选显式地告诉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将会是一个不错的体验。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值