Unity UI架構(gòu)設(shè)計理念
1.以ARPG為例,多個場景會反復(fù)出現(xiàn)相同的“UI窗體”,造成多個場景中反復(fù)加載相同的UI窗體。
解決方案:
????????????????“UI框架” 需要緩存項目(例游戲項目)中常用的“UI窗體"。
2.開發(fā)商業(yè)復(fù)雜項目時,各個UI(UI腳本)之間傳值,容易出現(xiàn)“緊耦合”(相互交錯,你中有我,我中有你)的情況。
解決方案:
????????????????各個UI的生成、銷毀、切換,都是通過框架(Manger)實現(xiàn),各個UI之間不直接聯(lián)系(傳值)。
3.卡牌、RPG等游戲類型項目,很多情況下會出現(xiàn)“UI窗體” 疊加現(xiàn)象。開發(fā)人員需要“手工”維護窗體中間的層級關(guān)系。
解決方案:
????????????????設(shè)計UI框架系統(tǒng),使用“棧”的數(shù)據(jù)結(jié)構(gòu),保存與控制當(dāng)前所有需要顯示的“UI窗體”的層級關(guān)系。
4.商業(yè)開發(fā)項目中的多個“UI窗體” 之間疊加出現(xiàn)時,必須保持“模態(tài)窗口”類型,否則容易出現(xiàn)誤操作。
解決方案??
設(shè)計的框架本身,需要對當(dāng)前顯示的窗體做“遮擋處理”,即:不允許用戶繞過當(dāng)前
“UI窗體”直接操作底層窗體,或者誤操作點擊項目中的3D游戲?qū)ο蟮?/p>
5.從以上問題還可以推導(dǎo)出如下“UI框架”需要注意的設(shè)計問題:
????????UI框架,需要管理加載“窗體預(yù)設(shè)”,進行自動加載的管理
????????UI框架,需要支持不同的語言環(huán)境,即語言的國際化。
? ????? 最后設(shè)計UI框架一個總的核心原則是:????
????????盡量讓框架本身完成與具體業(yè)務(wù)無關(guān)的事務(wù)性工作,讓開發(fā)人員只需要專注游戲業(yè)務(wù)邏輯的開發(fā)即可。(這個原則同樣適用于其他框架的設(shè)計中)
首先開發(fā)最簡版本功能設(shè)計:
??1:窗體自動加載管理。
??2:緩存UI窗體。
??3:窗體生命周期管理。
UI框架的核心類設(shè)計
??1: BaseUIForms 基礎(chǔ)UI窗體(父類)
??2: UIManger.cs UI窗體管理器
??3: UIType 窗體類型
??4: SysDefine 系統(tǒng)定義類
在Unity5.5安裝目錄下,建立腳本模版。
建立必要的目錄結(jié)構(gòu)與核心類,導(dǎo)入素材。
?BaseUIForms.cs
?UIManager.cs
?UIType.cs
?SysDefine.cs?[Config目錄下]
導(dǎo)入UI貼圖素材。
導(dǎo)入一些簡單的UI素材即可。
建立框架中的三個重要枚舉類型,定義 UIType 類。
??????UIFormsType?? UI窗體(位置)類型
??????UIFormsShowMode? UI窗體顯示類型
??????UIFormsLucencyType? 窗體透明度類型
?[提示: SysDefine 中定義]
定義“基礎(chǔ)UI窗體” BaseUIForms
定義 “UI管理器”? UIManager
? ? ? ?1: 定義“窗體路徑”與“窗體預(yù)設(shè)”的集合字段。
? ? ? ?2:定義“窗體預(yù)設(shè)”與管理腳本加載用的節(jié)點對象。
???????普通節(jié)點、固定節(jié)點、彈出節(jié)點、管理腳本節(jié)點。
? ? ? ?3:Unity編輯器中,定義“Canvas 根窗體”預(yù)設(shè)。
???????????????1>在測試場景中建立UI Panel 。
???????????????2>共建立3個Panel,與一個掛載腳本的空對象。
???????????????3>建立UI攝像機,設(shè)置參數(shù)。
???????????????4> Game視圖定義800*600 分辨率。
???????????????5>針對Canvas UI對象,設(shè)置合理參數(shù)
定義 “UI管理器”? UIManager (續(xù))
?4:定義“登陸窗體”、“選擇角色窗體”、“主窗體”等。
??????????1>建立各個窗體的Panel (注意:必須在Canvas 內(nèi)部建立)
??????????2>給各個窗體添加背景貼圖與必要按鈕等。
??????????3>定義的窗體都作為“預(yù)設(shè)”
定義 “UI管理器”? UIManager (續(xù))
?5:繼續(xù)開發(fā)UIManger 腳本,實現(xiàn)窗體的加載功能,且測試。
??????????1>主場景中,確保拖拽到層級視圖中的Canvas預(yù)設(shè)正確顯示。
??????????2>編寫Awake 事件函數(shù),對于常量都統(tǒng)一定義在SysDefine中。
??????????3>編寫“顯示UI窗體”公共方法。
??????????4>框架外建立測試腳本,測試UI窗體的基本加載功能。
?????????????1]建立框架外啟動加載腳本。
?????????????2]針對每個“窗體預(yù)設(shè)”都需要建立對應(yīng)的窗體腳本(繼承BaseUIForms)
?[備注:如果出現(xiàn)“baseUIForms==null, 請先確認(rèn)克隆對象上是否加載了BaseUIForms的子類”,說明“窗體預(yù)設(shè)”上必須添加BaseUIForms 的子類]
}定義 “UI管理器”? UIManager (續(xù))
?5:繼續(xù)開發(fā)UIManger 腳本,實現(xiàn)窗體的加載功能,且測試。(續(xù))
??5>? 測試以上所有步驟,成功如下圖所屬。
窗體層級管理
什么是“棧”數(shù)據(jù)結(jié)構(gòu)?
??是一種“先進后出”的數(shù)據(jù)結(jié)構(gòu),是一種常用算法。
??生活中的“漢諾塔”游戲、“摞燒餅”、“盤子堆”都是一種典型的“棧”結(jié)構(gòu)。
C#語言中提供 Stack泛型集合,來直接實現(xiàn)這種結(jié)構(gòu)。
常用屬性與方法:
Count 屬性?查詢棧內(nèi)元素數(shù)量
Push()?????壓棧
Pop()??????出棧
Peek()?????查詢棧頂元素
nGetEnumerator() 遍歷棧中所有元素
??演示:典型Demo示例。
開發(fā)“UI管理器”的“棧”數(shù)據(jù)結(jié)構(gòu),維護窗體的層級結(jié)構(gòu)。
定義Stack 類型字段。
顯示UI窗體 ShowUIForms() 方法中
????????1>“反向切換”屬性的窗體,定義“壓棧”方法
關(guān)閉(或返回上一個UI)窗體方法中
????????1>“普通”顯示屬性的窗體,定義關(guān)閉方法。
????????2>對于“反向切換”屬性的窗體,定義返回上一個窗體的方法(即:關(guān)閉)。
顯示UI窗體 ShowUIForms() 方法中
???????? 1>“反向切換”屬性窗體,定義“壓棧”方法
???????? 2> “隱藏其他”屬性窗體,定義顯示業(yè)務(wù)邏輯方法
關(guān)閉(或返回上一個UI)窗體方法中
???? ????1>“普通”顯示屬性的窗體,定義關(guān)閉方法。
???????? 2>對于“反向切換”屬性的窗體,定義返回上一個窗體的方法。 (即:關(guān)閉)。
????????3>“隱藏其他”屬性窗體,定義關(guān)閉邏輯方法
在多個UI業(yè)務(wù)窗體中,有時候需要客戶端程序主動清空“棧集合”中的當(dāng)前數(shù)據(jù),防止業(yè)務(wù)邏輯混亂。
?例如: RPG中的“商場系統(tǒng)”、“背包系統(tǒng)”、“任務(wù)系統(tǒng)”等。
具體代碼實現(xiàn):
??1:在UIType 類中,定義是否需要“清空反向切換”的字段(或者屬性)。
?2: 在UI管理器腳本中,關(guān)于顯示UI窗體的方法中,加入判斷清空棧中數(shù)據(jù)的業(yè)務(wù)邏輯即可。
定義如下窗體編寫代碼測試UI框架功能:
?登陸窗體:
????????注意事項: 所有窗體腳本都要繼承BaseUIForms
????????定義本窗體的類型(位置、顯示、透明度三大屬性),不寫則采用默認(rèn)數(shù)值。
? ? `? ? 注冊窗體按鈕事件。
?選擇英雄窗體:
?主城窗體:
?商城窗體:
?商品信息窗體:???
?程序重構(gòu)發(fā)現(xiàn),在UI框架內(nèi)部與客戶調(diào)用程序中都存在一些反復(fù)被使用的技術(shù)。
????????對于層級視圖的節(jié)點查找。
?????????(擴展方法)
????????獲取子節(jié)點(物體)的腳本
????????給子節(jié)點(物體)添加腳本
????????給子節(jié)點(物體)添加父對象
?程序重構(gòu)發(fā)現(xiàn),在客戶程序(UI框架)調(diào)用中,會反復(fù)出現(xiàn)一些常用的定義方式,例如:
????????按鈕的事件監(jiān)聽與注冊方法????
????????打開指定窗體
????????關(guān)閉指定窗體
????????發(fā)送消息
????????顯示語言信息
模態(tài)窗體管理
UI窗體中,很多彈出窗體要求玩家不能點擊“父窗體”,這就是“模態(tài)窗體”。
這里我們設(shè)計了四種模式類型:
? ? ? ? 完全透明、半透明、低透明度、透明且可以穿透。
在Canvas根窗體預(yù)設(shè)中(PopUp節(jié)點下)定義“UI遮擋面板”(_UIMaskPanel)窗體。
“UI遮擋面板” 就是一個普通的Panel。
平時這個面板是“不可見”狀態(tài)。
當(dāng)需要進行“模態(tài)”顯示的時候,則定義腳本,控制其在PopUp節(jié)點下倒數(shù)第二的位置,起到遮擋作用。
定義一個專門的控制腳本:“UIMaskMgr.cs”,以及在“窗體基類”(BaseUIForms.cs) 中控制“遮擋面板”的顯示與隱藏。
框架配置管理
所謂“配置管理”是指一個游戲項目(軟件項目),很多需要經(jīng)常變化的需求或者數(shù)據(jù),最好以配置文件的形式存在,從而代替“硬編碼”方式。
? 例如: 游戲項目語言的國際化、
???????? 日志文件的保存路徑等。
目前(2017)國際國內(nèi)普遍采用的配置管理方式主要有兩種: XML與Json 方式。
??兩者各有優(yōu)缺點:
? ? ? ? ? ?XML:對于數(shù)據(jù)的精確表示、易讀性很高。
?????????? 微軟很多的項目都內(nèi)置對XML作為配置文件的支持。
?????????(例如:網(wǎng)站項目:ASP.Net、 WinForm 等)
?????????? 缺點是讀寫速度慢,這個問題在移動端尤其突出。
??Json: 讀寫速度快,但是易讀性沒有XML好,但是可以接受。
?????????? 所以本框架項目都采用Json作為配置文件。
JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式。 JSON采用完全獨立于語言的文本格式,但是也使用了類似于C語言家族的習(xí)慣(包括C、C++、C#、Java、JavaScript、Perl、Python等)。這些特性使JSON成為理想的數(shù)據(jù)交換語言。 易于人閱讀和編寫,同時也易于機器解析和生成(一般用于提升網(wǎng)絡(luò)傳輸速率)。
JSON 語法 (JSON 語法是 JavaScript 對象表示語法的子集)
特點:
?數(shù)據(jù)在鍵值對中,數(shù)據(jù)由逗號分隔。
?花括號保存對象,方括號保存數(shù)組。
JSON 數(shù)據(jù)的書寫格式是:名稱/值對。 "firstName":"John"
開發(fā)Json配置管理器
定義通用配置管理器接口
開發(fā)實現(xiàn)IConfigManager 接口的通用配置管理器
UI管理器中關(guān)于“UI窗體預(yù)設(shè)路徑”集合中,把前面“硬編碼”改為應(yīng)用Json 配置管理的方式。
?????第1步在Resources 目錄下建立關(guān)于“UIFormsConfigInfo”的Json 文件。
?????第2步對于UIManager.cs 中的“UI窗體預(yù)設(shè)路徑”集合做配置管理。
日志調(diào)試
日志調(diào)試在游戲的開發(fā)全過程中占有非常重要的作用。
自定義“日志調(diào)試”腳本插件的開發(fā)思路與具體實現(xiàn)。
??現(xiàn)在為了更好的適用于PC與移動端調(diào)試的目的,進行再次重構(gòu)。
??第1:對于讀寫文件內(nèi)部方法做重構(gòu)完善,使得日志文件實現(xiàn)自動偵測與創(chuàng)建寫入操作等。
??第2: 改以前的針對XML的配置文件的讀取方式為Json 文件的讀取。
??目的一:提高讀取速度。
? ? ? ? ?二: 更好的應(yīng)用在移動端的部署
消息傳遞中心
基于Unity技術(shù)的游戲與項目研發(fā),目前提供的消息傳遞方式種類少,且耦合性很高。
?1:腳本組件公共方法、字段的相互調(diào)用。
????????例如: GetComponnet().TestMethod();
?2:SendMesage 技術(shù)。
?3:單例模式數(shù)據(jù)傳遞。
開發(fā)一種低耦合,無需考慮被傳遞對象(腳本名稱、組件名稱)的技術(shù)非常有價值。
“消息傳遞中心”:
??基于觀察者模式,利用委托與事件的基本機制原理,進一步封裝重構(gòu)的技術(shù)實現(xiàn)。
定義MessageCenter
?基本原理:
窗體基類(BaseUIForms) 中對于“消息傳遞中心”類常用方法的封裝。
?????發(fā)送消息 SendMessage()
?????接收消息 ReceiveMessage()
客戶程序消息傳遞多組數(shù)據(jù)的演示。
客戶程序建立“系統(tǒng)常量”類,方便程序復(fù)用與集中化管理
資源國際化
“資源國際化”對于游戲項目開發(fā)是指:語言、語音、貼圖、模型等國際化問題。
對于游戲項目,最常見的是針對不同國家的多語言版本的開發(fā),也就是“語言的國際化”。??
多語言版本的實現(xiàn),最基本的原理就是根據(jù)ID去讀取語言配置表,不同的語言新建一個語言配置表。
定義“語言管理器”(LanguageMgr)
基本原理:
??????1: 使用配置管理器腳本(繼承 IConfigManager接口),讀取不同語言的Json配置文件。
? ? ? 2: 使用 Dictionary 集合緩存“語言鍵值對”。
????? 3:定義顯示方法,根據(jù)ID查詢出對應(yīng)的語言信息
UI窗體基類(BaseUIForms) 對顯示語言的重構(gòu)。