游戏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 的核心类。

  1. /***
  2. *
  3. * Title: "SUIFW" UI框架项目
  4. * 主题: 消息(传递)中心
  5. * Description:
  6. * 功能: 负责UI框架中,所有UI窗体中间的数据传值。
  7. *
  8. * Date: 2017
  9. * Version: 0.1版本
  10. * Modify Recoder:
  11. *
  12. *
  13. */
  14. using System.Collections;
  15. using System.Collections.Generic;
  16. using UnityEngine;
  17.  
  18. namespace SUIFW
  19. {
  20. public class MessageCenter {
  21. //委托:消息传递
  22. public delegate void DelMessageDelivery(KeyValuesUpdate kv);
  23.  
  24. //消息中心缓存集合
  25. //<string : 数据大的分类,DelMessageDelivery 数据执行委托>
  26. public static Dictionary<string, DelMessageDelivery> _dicMessages = new Dictionary<string, DelMessageDelivery>();
  27.  
  28. /// <summary>
  29. /// 增加消息的监听。
  30. /// </summary>
  31. /// <param name="messageType">消息分类</param>
  32. /// <param name="handler">消息委托</param>
  33. public static void AddMsgListener(string messageType,DelMessageDelivery handler)
  34. {
  35. if (!_dicMessages.ContainsKey(messageType))
  36. {
  37. _dicMessages.Add(messageType,null);
  38. }
  39. _dicMessages[messageType] += handler;
  40. }
  41.  
  42. /// <summary>
  43. /// 取消消息的监听
  44. /// </summary>
  45. /// <param name="messageType">消息分类</param>
  46. /// <param name="handele">消息委托</param>
  47. public static void RemoveMsgListener(string messageType,DelMessageDelivery handele)
  48. {
  49. if (_dicMessages.ContainsKey(messageType))
  50. {
  51. _dicMessages[messageType] -= handele;
  52. }
  53.  
  54. }
  55.  
  56. /// <summary>
  57. /// 取消所有指定消息的监听
  58. /// </summary>
  59. public static void ClearALLMsgListener()
  60. {
  61. if (_dicMessages!=null)
  62. {
  63. _dicMessages.Clear();
  64. }
  65. }
  66.  
  67. /// <summary>
  68. /// 发送消息
  69. /// </summary>
  70. /// <param name="messageType">消息的分类</param>
  71. /// <param name="kv">键值对(对象)</param>
  72. public static void SendMessage(string messageType,KeyValuesUpdate kv)
  73. {
  74. DelMessageDelivery del; //委托
  75.  
  76. if (_dicMessages.TryGetValue(messageType,out del))
  77. {
  78. if (del!=null)
  79. {
  80. //调用委托
  81. del(kv);
  82. }
  83. }
  84. }
  85.  
  86. }
  87.  
  88. /// <summary>
  89. /// 键值更新对
  90. /// 功能: 配合委托,实现委托数据传递
  91. /// </summary>
  92. public class KeyValuesUpdate
  93. { //键
  94. private string _Key;
  95. //值
  96. private object _Values;
  97.  
  98. /* 只读属性 */
  99.  
  100. public string Key
  101. {
  102. get { return _Key; }
  103. }
  104. public object Values
  105. {
  106. get { return _Values; }
  107. }
  108.  
  109. public KeyValuesUpdate(string key, object valueObj)
  110. {
  111. _Key = key;
  112. _Values = valueObj;
  113. }
  114. }
  115.  
  116. }

以上核心原理解释如下:

  • 定义消息体的委托:public delegate void DelMessageDelivery(KeyValuesUpdate kv);    其中 KeyValuesUpdate 是一个简单的“键值对”类。
  • 定义“字典集合”,string 参数表示消息的分类与名称, DelMessagDelivery 表示对应消息名称的委托。

public static Dictionary<string, DelMessageDelivery> _dicMessages = new Dictionary<string, DelMessageDelivery>();
为了进一步简化与方便消息传递的使用,笔者又对MessageCenter 类中的部分核心方法,做了进一步封装:

  1. /// <summary>
  2. /// 发送消息
  3. /// </summary>
  4. /// <param name="msgType">消息的类型</param>
  5. /// <param name="msgName">消息名称</param>
  6. /// <param name="msgContent">消息内容</param>
  7. protected void SendMessage(string msgType,string msgName,object msgContent)
  8. {
  9. KeyValuesUpdate kvs = new KeyValuesUpdate(msgName,msgContent);
  10. MessageCenter.SendMessage(msgType, kvs);
  11. }
  12.  
  13. /// <summary>
  14. /// 接收消息
  15. /// </summary>
  16. /// <param name="messagType">消息分类</param>
  17. /// <param name="handler">消息委托</param>
  18. public void ReceiveMessage(string messagType,MessageCenter.DelMessageDelivery handler)
  19. {
  20. MessageCenter.AddMsgListener(messagType, handler);
  21. }

以上的代码被定义在“BaseUIForm”脚本中,前面介绍过这个脚本。 具体的“消息中心”应用示例如下:

本项目中当玩家点击“商城系统”中的最左边道具,系统会弹窗显示“神杖”道具,如果点击左边第2个道具图标,则系统显示“战靴”道具,具体见下图:

以上功能的实现代码如下:

  1. /***
  2. *
  3. * Title: "SUIFW" UI框架项目
  4. * 主题: “商城窗体”
  5. * Description:
  6. * 功能:
  7. *
  8. * Date: 2017
  9. * Version: 0.1版本
  10. * Modify Recoder:
  11. *
  12. *
  13. */
  14. using System.Collections;
  15. using System.Collections.Generic;
  16. using SUIFW;
  17. using UnityEngine;
  18.  
  19. namespace DemoProject
  20. {
  21. public class MarketUIFrom : BaseUIForm
  22. {
  23. void Awake ()
  24. {
  25. //窗体性质
  26. CurrentUIType.UIForms_Type = UIFormType.PopUp; //弹出窗体
  27. CurrentUIType.UIForm_LucencyType = UIFormLucenyType.Translucence;
  28. CurrentUIType.UIForms_ShowMode = UIFormShowMode.ReverseChange;
  29.  
  30. //注册按钮事件:退出
  31. RigisterButtonObjectEvent("Btn_Close",
  32. P=> CloseUIForm()
  33. );
  34. //注册道具事件:神杖
  35. RigisterButtonObjectEvent("BtnTicket",
  36. P =>
  37. {
  38. //打开子窗体
  39. OpenUIForm(ProConst.PRO_DETAIL_UIFORM);
  40. //传递数据
  41. string[] strArray = new string[] { "神杖详情", "神杖详细介绍。。。" };
  42. SendMessage("Props", "ticket", strArray);
  43. }
  44. );
  45.  
  46. //注册道具事件:战靴
  47. RigisterButtonObjectEvent("BtnShoe",
  48. P =>
  49. {
  50. //打开子窗体
  51. OpenUIForm(ProConst.PRO_DETAIL_UIFORM);
  52. //传递数据
  53. string[] strArray = new string[] { "战靴详情", "战靴详细介绍。。。" };
  54. SendMessage("Props", "shoes", strArray);
  55. }
  56. );
  57.  
  58. //注册道具事件:盔甲
  59. RigisterButtonObjectEvent("BtnCloth",
  60. P =>
  61. {
  62. //打开子窗体
  63. OpenUIForm(ProConst.PRO_DETAIL_UIFORM);
  64. //传递数据
  65. string[] strArray = new string[] { "盔甲详情", "盔甲详细介绍。。。" };
  66. SendMessage("Props", "cloth", strArray);
  67. }
  68. );
  69. }
  70.  
  71. /***
  72. *
  73. * Title: "SUIFW" UI框架项目
  74. * 主题: 道具详细信息窗体
  75. * Description:
  76. * 功能: 显示各种道具信息
  77. *
  78. * Date: 2017
  79. * Version: 0.1版本
  80. * Modify Recoder:
  81. *
  82. *
  83. */
  84. using System.Collections;
  85. using System.Collections.Generic;
  86. using System.Net.Mime;
  87. using SUIFW;
  88. using UnityEngine;
  89. using UnityEngine.UI;
  90.  
  91. namespace DemoProject
  92. {
  93. public class PropDetailUIForm : BaseUIForm
  94. {
  95. public Text TxtName; //窗体显示名称
  96.  
  97. void Awake ()
  98. {
  99. //窗体的性质
  100. CurrentUIType.UIForms_Type = UIFormType.PopUp;
  101. CurrentUIType.UIForms_ShowMode = UIFormShowMode.ReverseChange;
  102. CurrentUIType.UIForm_LucencyType = UIFormLucenyType.Translucence;
  103.  
  104. /* 按钮的注册 */
  105. RigisterButtonObjectEvent("BtnClose",
  106. p=>CloseUIForm()
  107. );
  108.  
  109. /* 接受信息 */
  110. ReceiveMessage("Props",
  111. p =>
  112. {
  113. if (TxtName)
  114. {
  115. string[] strArray = p.Values as string[];
  116. TxtName.text = strArray[0];
  117. //print("测试道具的详细信息: "+strArray[1]);
  118. }
  119. }
  120. );
  121.  
  122. }//Awake_end
  123.  
  124. }
  125. }

好了就先讲到这里,大家如有疑问,可以直接留言。下次讲解本框架项目的最后一篇: 游戏UI框架设计(7):资源国际化技术。

游戏UI框架设计(6): 消息传递中心的更多相关文章

  1. 游戏UI框架设计(一) : 架构设计理论篇

    游戏UI框架设计(一) ---架构设计理论篇 前几天(2017年2月)看到一篇文章,国内王健林.马云等大咖们看好的未来十大最有"钱途"产业中,排名第一的就是"泛娱乐&qu ...

  2. 游戏UI框架设计(五): 配置管理与应用

    游戏UI框架设计(五) --配置管理与应用 在开发企业级游戏/VR/AR产品时候,我们总是希望可以总结出一些通用的技术体系,框架结构等,为简化我们的开发起到"四两拨千金"的作用.所 ...

  3. 游戏UI框架设计(二) : 最简版本设计

    游戏UI框架设计(二) --最简版本设计 为降低难度决定先讲解一个最简版本,阐述UI框架的核心设计理念.这里先定义三个核心功能: 1:UI窗体的自动加载功能. 2:缓存UI窗体. 3:窗体生命周期(状 ...

  4. 游戏UI框架设计(三) : 窗体的层级管理

    游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...

  5. 游戏UI框架设计(四) : 模态窗体管理

    游戏UI框架设计(四) --模态窗体管理 我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗 ...

  6. 游戏UI框架设计(7): 资源国际化技术

    游戏UI框架设计(7) --资源国际化技术 说起"资源国际化"技术,个人认为可以追述到微软Window2000 PC操作系统的发布,在这之前windows98操作系统的开发都是先由 ...

  7. 《开源框架那些事儿22》:UI框架设计实战

    UI是User Interface的缩写.通常被觉得是MVC中View的部分,作用是提供跟人机交互的可视化操作界面. MVC中Model提供内容给UI进行渲染,用户通过UI框架产生响应,一般而言会由控 ...

  8. 自己动手设计并实现一个linux嵌入式UI框架(设计)

    看了"自己动手设计并实现一个linux嵌入式UI框架"显然没有尽兴,因为还没有看到庐山真面目,那我今天继续,先来说说,我用到了哪些知识背景.如:C语言基础知识,尤其是指针.函数指针 ...

  9. Cocos Creator 通用框架设计 —— 资源管理优化

    接着<Cocos Creator 通用框架设计 -- 资源管理>聊聊资源管理框架后续的一些优化: 通过论坛和github的issue,收到了很多优化或bug的反馈,基本上抽空全部处理了,大 ...

随机推荐

  1. 前后台分离部署时,Niginx上的部署

    upstream bowenpay_backend { server 127.0.0.1:9002; } server { listen 80; server_name wx.bowenpay.com ...

  2. 基于分支限界法的旅行商问题(TSP)二

    和上篇一样,考前写写伪代码,考完了补上具体的解释和代码. 状态{矩阵,结果集,下界} 全局结果集列表,全局上界初始为Infinite 建立一个heap,存储状态,出堆规则为拥有最小的下界. 利用red ...

  3. Flask入门之Virtualvenv的安装及使用(windows)

    Virtualvenv 提供一个特定的Python虚拟环境(沙盒),以便于那些要求特定版本的模块的脚本能够顺利运行. 因为在Virtualvenv中,我们可以使用 pip install -r req ...

  4. C语言中变量的存储方式

    变量可以分为全局变量.静态全局变量.局部变量和静态局部变量变量的声明有两种情况:1)一种是需要建立存储空间的(定义性声明).例如int a 在生命的时候就已经建立了存储空间.2)另一种是不需要建立存储 ...

  5. nexus私服搭建及maven生命周期

    一.maven找库流程 从流程上看创建nexus私服,能够优化流程,而且更加快速 二.nexus下载.安装 1.nexus下载地址 https://sonatype-download.global.s ...

  6. ansj原子切分和全切分

    ansj第一步会进行原子切分和全切分,并且是在同时进行的.所谓原子,是指短句中不可分割的最小语素单位.例如,一个汉字就是一个原子.全切分,就是把一句话中的所有词都找出来,只要是字典中有的就找出来.例如 ...

  7. ES6-LET,变量提升,函数提升

    1:let命令 ①类似var,但只在let所在代码块内有效 ②不存在变量提升 ③暂时性死区(TDZ)—有let命令时,在此命令前都没法使用此变量 ④不允许重复声明 ⑤ES6允许块级作用域任意嵌套 ⑥E ...

  8. 关于linux find命令的使用

    find 和 xargs   xargs和find 在 使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行.但有些系统对能够传递给exec的命令 ...

  9. ASP.NET Core 2 学习笔记(一)

    来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽然名称沿用ASP.NET,但相对于ASP.NET确实有许多架构上的差异,可以说除了 ...

  10. 深入理解SpringCloud之分布式配置

    Spring Cloud Config Server能够统一管理配置,我们绝大多数情况都是基于git或者svn作为其配置仓库,其实SpringCloud还可以把数据库作为配置仓库,今天我们就来了解一下 ...