游戏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):资源国际化技术。