游戲UI框架設計(6)
--消息傳遞中心
最近一直忙於一個益智類游戲的研發工作,所以博客有段時間沒有更新了。經過朋友的督促,決定這兩天立刻完成最后的兩篇博客講解(UI框架)。
說起“消息傳遞中心”,或者是“消息中心”,熟悉一些客戶端架構設計的朋友一定不陌生。這種技術的來源就是為了解決腳本、類之間緊耦合的問題,而誕生的一種開發思想。目前基於Unity技術的游戲與項目研發,目前官方提供的消息傳遞方式種類少,且耦合性很高。
例如以下三種常用數據傳遞方式:
- 1: 腳本組件公共方法、字段的相互調用。
eg: GetComponnet<Scripts>().TestMethod();
這種方式是Unity提供初學者最簡單,易用的方式,但是缺點很明顯。
1> 寫法麻煩,效率低。
2> 腳本之間存在強耦合性。
- 2: 單例模式數據傳遞。
使用“單例模式”做腳本(類)之間的數據傳遞,例如本UI框架中的UIMaskMgr.cs ResourcesMgr.cs 腳本都應用了單例。此種模式優劣分析如下:
1> 突出優點:腳本(類)之間可以非常簡單方便的進行數據互相調用,且效率高。
2> 缺點: 腳本之間的強耦合性。
本數據傳遞方式,一般大家都在設計框架的內部應用,一般不會應用在實戰項目中。(因為框架內部相對穩定,而實戰項目需求經常變化)
- 3:SendMesage 技術。
SendMessage 是Unity 官方推薦的一種數據低耦合方式,但用過的朋友知道這種方式使用麻煩、適用范圍狹小、且存在一定耦合性。
所以開發一種低耦合性,無需考慮腳本(類)內部差異(腳本名稱、組件名稱)的技術非常有價值。於是筆者基於觀察者設計模式,利用委托與事件的基本機制原理,進一步封裝重構了一個 MessageCenter.cs 的核心類。
/***
*
* Title: "SUIFW" UI框架項目
* 主題: 消息(傳遞)中心
* Description:
* 功能: 負責UI框架中,所有UI窗體中間的數據傳值。
*
* Date: 2017
* Version: 0.1版本
* Modify Recoder:
*
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace SUIFW
{
public class MessageCenter {
//委托:消息傳遞
public delegate void DelMessageDelivery(KeyValuesUpdate kv);
//消息中心緩存集合
//<string : 數據大的分類,DelMessageDelivery 數據執行委托>
public static Dictionary<string, DelMessageDelivery> _dicMessages = new Dictionary<string, DelMessageDelivery>();
/// <summary>
/// 增加消息的監聽。
/// </summary>
/// <param name="messageType">消息分類</param>
/// <param name="handler">消息委托</param>
public static void AddMsgListener(string messageType,DelMessageDelivery handler)
{
if (!_dicMessages.ContainsKey(messageType))
{
_dicMessages.Add(messageType,null);
}
_dicMessages[messageType] += handler;
}
/// <summary>
/// 取消消息的監聽
/// </summary>
/// <param name="messageType">消息分類</param>
/// <param name="handele">消息委托</param>
public static void RemoveMsgListener(string messageType,DelMessageDelivery handele)
{
if (_dicMessages.ContainsKey(messageType))
{
_dicMessages[messageType] -= handele;
}
}
/// <summary>
/// 取消所有指定消息的監聽
/// </summary>
public static void ClearALLMsgListener()
{
if (_dicMessages!=null)
{
_dicMessages.Clear();
}
}
/// <summary>
/// 發送消息
/// </summary>
/// <param name="messageType">消息的分類</param>
/// <param name="kv">鍵值對(對象)</param>
public static void SendMessage(string messageType,KeyValuesUpdate kv)
{
DelMessageDelivery del; //委托
if (_dicMessages.TryGetValue(messageType,out del))
{
if (del!=null)
{
//調用委托
del(kv);
}
}
}
}
/// <summary>
/// 鍵值更新對
/// 功能: 配合委托,實現委托數據傳遞
/// </summary>
public class KeyValuesUpdate
{ //鍵
private string _Key;
//值
private object _Values;
/* 只讀屬性 */
public string Key
{
get { return _Key; }
}
public object Values
{
get { return _Values; }
}
public KeyValuesUpdate(string key, object valueObj)
{
_Key = key;
_Values = valueObj;
}
}
}
以上核心原理解釋如下:
- 定義消息體的委托:public delegate void DelMessageDelivery(KeyValuesUpdate kv); 其中 KeyValuesUpdate 是一個簡單的“鍵值對”類。
- 定義“字典集合”,string 參數表示消息的分類與名稱, DelMessagDelivery 表示對應消息名稱的委托。
public static Dictionary<string, DelMessageDelivery> _dicMessages = new Dictionary<string, DelMessageDelivery>();
為了進一步簡化與方便消息傳遞的使用,筆者又對MessageCenter 類中的部分核心方法,做了進一步封裝:
/// <summary>
/// 發送消息
/// </summary>
/// <param name="msgType">消息的類型</param>
/// <param name="msgName">消息名稱</param>
/// <param name="msgContent">消息內容</param>
protected void SendMessage(string msgType,string msgName,object msgContent)
{
KeyValuesUpdate kvs = new KeyValuesUpdate(msgName,msgContent);
MessageCenter.SendMessage(msgType, kvs);
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="messagType">消息分類</param>
/// <param name="handler">消息委托</param>
public void ReceiveMessage(string messagType,MessageCenter.DelMessageDelivery handler)
{
MessageCenter.AddMsgListener(messagType, handler);
}
以上的代碼被定義在“BaseUIForm”腳本中,前面介紹過這個腳本。 具體的“消息中心”應用示例如下:

本項目中當玩家點擊“商城系統”中的最左邊道具,系統會彈窗顯示“神杖”道具,如果點擊左邊第2個道具圖標,則系統顯示“戰靴”道具,具體見下圖:


以上功能的實現代碼如下:
/***
*
* Title: "SUIFW" UI框架項目
* 主題: “商城窗體”
* Description:
* 功能:
*
* Date: 2017
* Version: 0.1版本
* Modify Recoder:
*
*
*/
using System.Collections;
using System.Collections.Generic;
using SUIFW;
using UnityEngine;
namespace DemoProject
{
public class MarketUIFrom : BaseUIForm
{
void Awake ()
{
//窗體性質
CurrentUIType.UIForms_Type = UIFormType.PopUp; //彈出窗體
CurrentUIType.UIForm_LucencyType = UIFormLucenyType.Translucence;
CurrentUIType.UIForms_ShowMode = UIFormShowMode.ReverseChange;
//注冊按鈕事件:退出
RigisterButtonObjectEvent("Btn_Close",
P=> CloseUIForm()
);
//注冊道具事件:神杖
RigisterButtonObjectEvent("BtnTicket",
P =>
{
//打開子窗體
OpenUIForm(ProConst.PRO_DETAIL_UIFORM);
//傳遞數據
string[] strArray = new string[] { "神杖詳情", "神杖詳細介紹。。。" };
SendMessage("Props", "ticket", strArray);
}
);
//注冊道具事件:戰靴
RigisterButtonObjectEvent("BtnShoe",
P =>
{
//打開子窗體
OpenUIForm(ProConst.PRO_DETAIL_UIFORM);
//傳遞數據
string[] strArray = new string[] { "戰靴詳情", "戰靴詳細介紹。。。" };
SendMessage("Props", "shoes", strArray);
}
);
//注冊道具事件:盔甲
RigisterButtonObjectEvent("BtnCloth",
P =>
{
//打開子窗體
OpenUIForm(ProConst.PRO_DETAIL_UIFORM);
//傳遞數據
string[] strArray = new string[] { "盔甲詳情", "盔甲詳細介紹。。。" };
SendMessage("Props", "cloth", strArray);
}
);
}
/***
*
* Title: "SUIFW" UI框架項目
* 主題: 道具詳細信息窗體
* Description:
* 功能: 顯示各種道具信息
*
* Date: 2017
* Version: 0.1版本
* Modify Recoder:
*
*
*/
using System.Collections;
using System.Collections.Generic;
using System.Net.Mime;
using SUIFW;
using UnityEngine;
using UnityEngine.UI;
namespace DemoProject
{
public class PropDetailUIForm : BaseUIForm
{
public Text TxtName; //窗體顯示名稱
void Awake ()
{
//窗體的性質
CurrentUIType.UIForms_Type = UIFormType.PopUp;
CurrentUIType.UIForms_ShowMode = UIFormShowMode.ReverseChange;
CurrentUIType.UIForm_LucencyType = UIFormLucenyType.Translucence;
/* 按鈕的注冊 */
RigisterButtonObjectEvent("BtnClose",
p=>CloseUIForm()
);
/* 接受信息 */
ReceiveMessage("Props",
p =>
{
if (TxtName)
{
string[] strArray = p.Values as string[];
TxtName.text = strArray[0];
//print("測試道具的詳細信息: "+strArray[1]);
}
}
);
}//Awake_end
}
}
好了就先講到這里,大家如有疑問,可以直接留言。下次講解本框架項目的最后一篇: 游戲UI框架設計(7):資源國際化技術。
