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 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將會是一個不錯的體驗。