淺談使用NGUI的界面架構(二)關於NData

在剛接觸NGUI的時候,我們一般會採用在腳本中獲取NGUI 組件的形式給NGUI 組件賦值。有兩種選擇:一種是在代碼中根據路徑獲取NGUI 組件;另一種是在場景中,直接把組件拖到腳本上。第一種方法的的缺點是需要維護NGUI組件的路徑,第二種方法的缺點是替換組件時總是需要重新拖組件。兩種方法都比較不方便,這裡用第二種來舉例。

打個比方,我們界面右上角要顯示玩家擁有的金幣總數。於是我們做了個UILabel拖進腳本,在腳本里給它的text賦值顯示當前金幣數量。

PagePlayer.cs中:

public UILabel goldLabel;n public void SetGold( string gold )n {n goldLabel.text = gold;n }n

後來策劃需求在左下角和右下角也要顯示金幣,於是我們又做了兩個uilabel放到相應位置,並在腳本里添加變數,把新加的uilabel拖到腳本里。每次金幣的值發生變化,就要找到所有的uilabel變數給他們一一賦值。

PagePlayer.cs中:

public UILabel goldLabel;n public UILabel goldLabel1;n public UILabel goldLabel2;n public void SetGold( string gold )n {n goldLabel.text = gold;n goldLabel1.text = gold;n goldLabel2.text = gold;n }n

然而可能以後又會增加其它顯示金幣的界面,每加一個金幣的UILabel,就要去腳本里增加一個UILabel 的變數然後在金幣變化的時候給它賦值。雖然麻煩,但也能把功能做出來。我們不能就此滿足,偷懶是提高生產力的最大動力。

現在想要簡化這個流程,就要實現以下功能。

1,不需要每次添加金幣文字的時候都在腳本中新增一個UILabel變數,並把對應的UILabel組件拖進來。

2,不需要每次修改金幣的時候都要找到所有的金幣UILabel變數去修改他們的值。

初步的解決方案是這樣子的:我希望腳本裡面有一個值 gold代表的是金幣數量。所有的金幣UILabel 都跟這個值產生關聯。只要修改這個值,所有跟他關聯的UILabel 都自動發生變化。另外,在我要添加一個金幣UILabel 的時候,我希望它能自動去找頁面腳本中的gold變數來發生關聯,而不需要我在腳本中改代碼。

具體實現的思路,就是在帶有UILabel腳本的物體上加一個腳本,使其與頁面腳本種的gold變數發生關聯。然後給gold變數加set方法,在這個方法中發一個消息,告知所有和gold有過關聯的的UILabel要發生值的改變。這樣每次給gold變數賦值的時候,所有與其關聯的UILabel就會自動更新顯示的內容。

本著不重複造輪子的原則,在疑似開始造輪子之前一定要Google一下。於是在網上搜出了MVVM模式,NData插件等等。並發現NData不僅可以用於UILabel,還可以用於各種NGUI組件,並有很好的綁定層級管理。

具體的用法這篇博客里已經寫的很清楚了

NGUI 學習筆記實戰之二——商城數據綁定(Ndata)

NData就是基於MVVM模式,其中用戶自定義繼承EZData.Context的類,就相當於是自定義ViewModel層的內容。

剩下的問題就是,怎樣用這個工具來管理頁面。

根據上一篇文章 淺談使用NGUI的界面架構(一),我把界面分成很多個TweenPage,然後在單例MainPageMgr中統一管理。對於數據,我希望把每個頁面的數據也獨立出來,即每個頁面有一個對應的繼承EZData.Context的類,這個頁面相關的數據都放在這個類中,然後再由MainPageMgr來統一管理。

例如有個遊戲頁面PageInGame,用來顯示遊戲中獲得的金幣,鑽石和星星。現在新建一個PageInGameContext繼承EZData.Context。現在PageInGame頁面就有一個model層PageInGame類和一個ViewModel層PageInGameContext類。View層自然就是PageInGame物體下面的NGUI組件了。這樣就形成了MVVM模式。

using UnityEngine;nusing System.Collections;nnpublic class PageInGameContext : EZData.Contextn{n #region Property Goldnn private readonly EZData.Property<int> _privateGoldProperty = new EZData.Property<int> ();nn public EZData.Property<int> GoldProperty { get { return _privateGoldProperty; } }nn public int Gold {n get { return GoldProperty.GetValue (); }n set { GoldProperty.SetValue (value); }n }nn #endregionnnn #region Property Diamondnn private readonly EZData.Property<int> _privateDiamondProperty = new EZData.Property<int> ();nn public EZData.Property<int> DiamondProperty { get { return _privateDiamondProperty; } }nn public int Diamond {n get { return DiamondProperty.GetValue (); }n set { DiamondProperty.SetValue (value); }n }nn #endregionnn #region Property Starnn private readonly EZData.Property<int> _privateStarProperty = new EZData.Property<int> ();nn public EZData.Property<int> StarProperty { get { return _privateStarProperty; } }nn public int Star {n get { return StarProperty.GetValue (); }n set { StarProperty.SetValue (value); }n }nn #endregionnn}nnnnpublic class PageInGame : TweenPage {nn public PageInGameContext Context;nn nn protected override void Awake ()n {n base.Awake ();nn MainPageMgr.instance.Context.pageInGame = this;n Context = MainPageMgr.instance.Context.pageInGameCtx;nn }nn nnn protected override void OnPreBringIn ()n {n base.OnPreBringIn ();n nn }nn protected override void OnPreBringOut ()n {n base.OnPreBringOut ();n n }n}n

在MainPageMgr中有一個MainView Context是用來管理所有頁面的Context(下文中繼承EZData.Context的類,都統稱為Context。):

public class MainPageMgr : PageMgrSingleton<MainPageMgr>n{nn public NguiRootContext View;n //這個代表頁面模型n public MainView Context;nnnn void Awake()n {n n Context = new MainView();n SetContext();nnn }nn public void SetContext()n {n View.SetContext(Context);n }n}n

MainView.cs

public class MainView : EZData.Contextn{n n public MainView()n { n pageInGameCtx = new PageInGameContext();n }nn #region viewModel n public PageInGameContext pageInGameCtx{ get; private set; } n #endregionnn #region modeln public PageInGame pageInGame;n #endregionn}n

這樣,所有的頁面都可以通過MainPageMgr.instance.Context來獲取所有頁面的Context,如pageXXXContext,也可以獲得所有頁面的邏輯腳本,如pagXXX。

在場景里,只需要把頁面放在MainPageMgr的下級,然後再通過Master path來綁定到MainPageMgr中的Context就可以了。

在開發中,可能出現不同的頁面共用相同的數據,這種情況就可以直接把兩個頁面的Master Path綁定到同一個Context上,這樣開發起來會方便很多。

總結:

引入NData這個插件,主要是為了減少一些對NGUI組件的操作(如獲取組件和賦值等),把所有的工作都簡化為改變Context中的值,來動態改變NGUI組件的顯示。把各個頁面的Context都統一管理,是為了更方便地獲取數據,但原則上不應該在A頁面的model層中去修改B頁面Context中的數據,因為這樣容易造成混亂。

使用NData加NGUI,可以很快速地搭建一套頁面框架。現在我已經把這兩個工具專門打成插件包,開發新項目時直接導進去用,非常方便。


推薦閱讀:

Unity3D熱更新LuaFramework入門實戰(7)——PureMVC
Unity自動化構建之iOS打包(另有Android篇)
微表面模型-PBR渲染管線的材質
Unity線性空間下移動設備上烘焙變暗問題處理
UWA GOT v1.1 | 支持本地管理深度測評、全新的UWA API、兼容Unity 2017.3

TAG:Unity游戏引擎 | 游戏开发 |