Unity3D 消息框架设计

先考虑需要实现的基本功能: 在任意一个地方增加一个消息的listener,则当这个消息被broadcast的时候,所有的listener都应该收到。
考虑到delegate(我的理解是本身就是为实现观察者模式而出现的类),另外有个dict保存消息名和Delegate的对应。

定义一个static消息类Messenger。

1 static internal class Messenger
2 {
3     private static Dictionary<string, Delegate> eventDict = new Dictionary<string,Delegate>();
4 }

考虑到委托需要全部代码可见,所以单独放入到一个文件中。

以下实现了4中委托,都是void返回。可以依据需求自己增加。

1     //无参数
2     public delegate void Callback();
3     //一个参数的模板
4     public delegate void Callback<T>(T t);
5     //两个参数的模板
6     public delegate void Callback<T, U>(T t, U u);
7
8     public delegate void Callback<T,U,V>(T t,U u,V v);

为了进行优化,在load一个新level的时候,考虑将原先的消息都清空。但是有一些消息可能需要一直存在。
所以给messenger 类添加了 CleanUp 函数,一个标记永久消息函数,和一个 list来存储永久消息

1    private static List<string> permenentMessages = new List<string>();
1    public static void SetPermenent(string name)
2     {
3         if(!permenentMessages.Contains(name))
4         {
5             permenentMessages.Add(name);
6         }
7     }
 1    public static void CleanUp()
 2     {
 3         foreach (var value in eventDict)
 4         {
 5             if (!permenentMessages.Contains(value.Key))
 6             {
 7                 eventDict.Remove(value.Key);
 8             }
 9         }
10     }

添加 MessengerHelp类

 1 public sealed class MessengerHelper:MonoBehaviour
 2 {
 3     private void Awake()
 4     {
 5         DontDestroyOnLoad(gameObject);
 6     }
 7
 8     public void OnLevelWasLoaded(int unUsed)
 9     {
10          //每次新level load 的时候,清理以前的message 。  但有些信息需要保留,通用,所以的话, 就必须有永久信息的存储。list
11         Messenger.CleanUp();
12     }
13
14 }

Messenger中其他的消息操作(增加,删除,广播,打印)(以无参数为例):

  1     /// <summary>
  2     /// 打印出所有事件
  3     /// </summary>
  4     public static void PrintEventDict()
  5     {
  6         Debug.Log("EventDict as follows");
  7         foreach(var pair in eventDict)
  8         {
  9             Debug.Log("Event name:  " + pair.Key);
 10             Debug.Log("Event Delegate:  " + pair.Value);
 11         }
 12     }
 13
 14
 15         public static void OnAdding(string name,Delegate listenerBeingAdded)
 16     {
 17         if(!eventDict.ContainsKey(name))
 18         {
 19             eventDict.Add(name, null);
 20         }
 21
 22         //判断加入的类型与原先存储的类型是否一样
 23         Delegate d = eventDict[name];
 24         if(d!=null && d.GetType() != listenerBeingAdded.GetType())
 25         {
 26             throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", name, d.GetType().Name, listenerBeingAdded.GetType().Name));
 27         }
 28     }
 29     static public void OnRemoving(string name,Delegate listenerBeingRemoved)
 30     {
 31         if(eventDict.ContainsKey(name))
 32         {
 33             Delegate d = eventDict[name];
 34             if(d == null)
 35             {
 36                 throw new ListenerException(string.Format("Attempting to remove listener with for name \"{0}\" but current listener is null.", name));
 37             }
 38             else if(d.GetType() != listenerBeingRemoved.GetType())
 39             {
 40                 throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for name {0}. Current listeners have type {1} and listener being removed has type {2}", name, d.GetType().Name, listenerBeingRemoved.GetType().Name));
 41
 42             }
 43         }
 44         else
 45         {
 46             throw new ListenerException(string.Format("Attempting to remove listener for name \"{0}\" but Messenger doesn‘t know about this name.", name));
 47         }
 48     }
 49     /// <summary>
 50     /// 每次移除后查看name对应的value是否为空
 51     /// </summary>
 52     /// <param name="name"></param>
 53     static public void OnRemoved(string name)
 54     {
 55         if(eventDict[name] == null)
 56         {
 57             eventDict.Remove(name);
 58         }
 59     }
 60     static public void OnBroadcasting(string name)
 61     {
 62 #if REQUIRE_LISTENER
 63         if (!eventDict.ContainsKey(name))
 64         {
 65             throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", name));
 66         }
 67 #endif
 68     }
 69
 70
 71        static public void AddListener(string name, Callback listenerBeingAdded)
 72     {
 73         OnAdding(name,listenerBeingAdded);
 74         //存储这个name 对应的delegate
 75         eventDict[name] = (Callback)eventDict[name] + listenerBeingAdded;
 76     }
 77
 78      static public void RemoveListener(string name, Callback ListenerBeingRemoved)
 79     {
 80         //移除前检查
 81         OnRemoving(name, ListenerBeingRemoved);
 82         eventDict[name] = (Callback)eventDict[name] - ListenerBeingRemoved;
 83         //移除后检查
 84         OnRemoved(name);
 85     }
 86
 87
 88  public static void BroadCast(string name)
 89     {
 90         OnBroadcasting(name);
 91
 92         Delegate d;
 93         if(eventDict.TryGetValue(name,out d))
 94         {
 95             Callback callback = d as Callback;
 96
 97             if(callback != null)
 98             {
 99                 callback();
100             }
101             else
102             {
103                 throw CreateBroadcastSignatureException(name);
104             }
105         }
106     }

使用方式:

 1 using UnityEngine;
 2 using System.Collections;
 3
 4 public class UseMessenger : MonoBehaviour
 5 {
 6     int health = 100;
 7
 8     void Awake()
 9     {
10         Messenger.AddListener("LoseHealth", LoseHealth);
11         Messenger.AddListener("LoseHealth", LoseHealthTwo);
12         Messenger.AddListener("LoseHealthLoseHealth", LoseHealth);
13         Messenger.AddListener<int, string, bool>("FOO", Foo);
14     }
15
16     void Update()
17     {
18         Debug.Log(health);
19     }
20
21     void LoseHealth()
22     {
23         health -= 10;
24     }
25
26     void LoseHealthTwo()
27     {
28         health -= 10;
29     }
30
31     void Foo(int number,string name,bool isTrue)
32     {
33             Debug.Log(number);
34             Debug.Log(name);
35             Debug.Log(isTrue);
36     }
37 }
 1 using UnityEngine;
 2 using System.Collections;
 3
 4 public class BroadCast : MonoBehaviour
 5 {
 6
 7     void Start()
 8     {
 9         Messenger.BroadCast("LoseHealth");
10         Messenger.SetPermenent("LoseHealth");
11         Messenger.BroadCast<int, string, bool>("FOO",100, "nice", true);
12
13         Messenger.PrintEventDict();
14
15     }
16 }

执行结果:

所有的代码如下。

  1 #define REQUIRE_LISTENER
  2
  3 using UnityEngine;
  4 using System.Collections;
  5 using System.Collections.Generic;
  6 using System;
  7
  8 /// <summary>
  9 ///  消息处理框架
 10 /// </summary>
 11 static internal class Messenger   //static internal
 12 {
 13
 14     static private MessengerHelper messengerHelper = (new GameObject("MessengerHelper")).AddComponent<MessengerHelper>();
 15     //存储暂时的消息,关卡load时清空
 16     //不同类型的delegate存储在同一个字典中,用大写的Delegate表示类型。 在System 下
 17     //此处value值只能为delegate,而不能选择event。 所以说只是利用了delegate 而没有使用event。为了简洁;
 18     private static Dictionary<string, Delegate> eventDict = new Dictionary<string,Delegate>();
 19     //存储永久的消息,清空时忽略list中的消息
 20     private static List<string> permenentMessages = new List<string>();
 21
 22     /// <summary>
 23     /// 打印出所有事件
 24     /// </summary>
 25     public static void PrintEventDict()
 26     {
 27         Debug.Log("EventDict as follows");
 28         foreach(var pair in eventDict)
 29         {
 30             Debug.Log("Event name:  " + pair.Key);
 31             Debug.Log("Event Delegate:  " + pair.Value);
 32         }
 33     }
 34
 35     /// <summary>
 36     /// 标记为永久有效
 37     /// </summary>
 38     /// <param name="name"></param>
 39     public static void SetPermenent(string name)
 40     {
 41         if(!permenentMessages.Contains(name))
 42         {
 43             permenentMessages.Add(name);
 44         }
 45     }
 46     /// <summary>
 47     /// 清理
 48     /// </summary>
 49     public static void CleanUp()
 50     {
 51         foreach (var value in eventDict)
 52         {
 53             if (!permenentMessages.Contains(value.Key))
 54             {
 55                 eventDict.Remove(value.Key);
 56             }
 57         }
 58     }
 59     public static void OnAdding(string name,Delegate listenerBeingAdded)
 60     {
 61         if(!eventDict.ContainsKey(name))
 62         {
 63             eventDict.Add(name, null);
 64         }
 65
 66         //判断加入的类型与原先存储的类型是否一样
 67         Delegate d = eventDict[name];
 68         if(d!=null && d.GetType() != listenerBeingAdded.GetType())
 69         {
 70             throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", name, d.GetType().Name, listenerBeingAdded.GetType().Name));
 71         }
 72     }
 73     static public void OnRemoving(string name,Delegate listenerBeingRemoved)
 74     {
 75         if(eventDict.ContainsKey(name))
 76         {
 77             Delegate d = eventDict[name];
 78             if(d == null)
 79             {
 80                 throw new ListenerException(string.Format("Attempting to remove listener with for name \"{0}\" but current listener is null.", name));
 81             }
 82             else if(d.GetType() != listenerBeingRemoved.GetType())
 83             {
 84                 throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for name {0}. Current listeners have type {1} and listener being removed has type {2}", name, d.GetType().Name, listenerBeingRemoved.GetType().Name));
 85
 86             }
 87         }
 88         else
 89         {
 90             throw new ListenerException(string.Format("Attempting to remove listener for name \"{0}\" but Messenger doesn‘t know about this name.", name));
 91         }
 92     }
 93     /// <summary>
 94     /// 每次移除后查看name对应的value是否为空
 95     /// </summary>
 96     /// <param name="name"></param>
 97     static public void OnRemoved(string name)
 98     {
 99         if(eventDict[name] == null)
100         {
101             eventDict.Remove(name);
102         }
103     }
104     static public void OnBroadcasting(string name)
105     {
106 #if REQUIRE_LISTENER
107         if (!eventDict.ContainsKey(name))
108         {
109             throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", name));
110         }
111 #endif
112     }
113
114     #region AddListener
115     static public void AddListener(string name, Callback listenerBeingAdded)
116     {
117         OnAdding(name,listenerBeingAdded);
118         //存储这个name 对应的delegate
119         eventDict[name] = (Callback)eventDict[name] + listenerBeingAdded;
120     }
121
122     public static void AddListener<T>(string name,Callback<T> listenerBeingAdded)
123     {
124         OnAdding(name, listenerBeingAdded);
125         eventDict[name] = (Callback<T>)eventDict[name] + listenerBeingAdded;
126     }
127     public static void AddListener<T,U>(string name,Callback<T,U> listenerBeingAdded)
128     {
129         OnAdding(name, listenerBeingAdded);
130         eventDict[name] = (Callback<T, U>)eventDict[name] + listenerBeingAdded;
131     }
132
133     public static void AddListener<T,U,V>(string name,Callback<T,U,V> listenerBeingAdded)
134     {
135         OnAdding(name, listenerBeingAdded);
136         eventDict[name] = (Callback<T, U, V>)eventDict[name] + listenerBeingAdded;
137     }
138     #endregion
139
140     #region RemoveListener
141     static public void RemoveListener(string name, Callback ListenerBeingRemoved)
142     {
143         //移除前检查
144         OnRemoving(name, ListenerBeingRemoved);
145         eventDict[name] = (Callback)eventDict[name] - ListenerBeingRemoved;
146         //移除后检查
147         OnRemoved(name);
148     }
149
150     static public void RemoveListener<T>(string name,Callback<T> ListenerBeingRemoved)
151     {
152         OnRemoving(name, ListenerBeingRemoved);
153         eventDict[name] = (Callback<T>)eventDict[name] - ListenerBeingRemoved;
154         OnRemoved(name);
155     }
156     public static void RemoveListener<T,U>(string name,Callback<T,U>listenerBeingRemoved)
157     {
158         OnRemoving(name, listenerBeingRemoved);
159         eventDict[name] = (Callback<T, U>)eventDict[name] - listenerBeingRemoved;
160         OnRemoved(name);
161
162     }
163
164     public static void RemoveListener<T,U,V>(string name,Callback<T,U,V>listenerBeingRemoved)
165     {
166         OnRemoving(name, listenerBeingRemoved);
167         eventDict[name] = (Callback<T, U, V>)eventDict[name] - listenerBeingRemoved;
168         OnRemoved(name);
169     }
170     #endregion
171
172     #region BroadCast
173
174     public static void BroadCast(string name)
175     {
176         OnBroadcasting(name);
177
178         Delegate d;
179         if(eventDict.TryGetValue(name,out d))
180         {
181             Callback callback = d as Callback;
182
183             if(callback != null)
184             {
185                 callback();
186             }
187             else
188             {
189                 throw CreateBroadcastSignatureException(name);
190             }
191         }
192     }
193
194     public static void BroadCast<T>(string name,T t)
195     {
196         OnBroadcasting(name);
197
198         Delegate d;
199         if(eventDict.TryGetValue(name,out d))
200         {
201             Callback<T> callback = d as Callback<T>;
202             if(callback != null)
203             {
204                 callback(t);
205             }
206             else
207             {
208                 throw CreateBroadcastSignatureException(name);
209             }
210         }
211
212     }
213     public static void BroadCast<T,U>(string name,T t,U u)
214     {
215         OnBroadcasting(name);
216         Delegate d;
217         if(eventDict.TryGetValue(name,out d))
218         {
219             Callback<T, U> callback = d as Callback<T, U>;
220             if(callback != null)
221             {
222                 callback(t,u);
223             }
224             else
225             {
226                 throw CreateBroadcastSignatureException(name);
227             }
228         }
229     }
230     public static void BroadCast<T,U,V>(string name,T t,U u,V v)
231     {
232         OnBroadcasting(name);
233         Delegate d;
234         if(eventDict.TryGetValue(name,out d))
235         {
236             Callback<T,U,V> callback = d as Callback<T,U,V>;
237             if(callback != null)
238             {
239                 callback(t, u, v);
240             }
241             else
242             {
243                 throw CreateBroadcastSignatureException(name);
244             }
245         }
246     }
247     #endregion
248
249     #region Exception
250     public class ListenerException:Exception
251     {
252         public ListenerException(string msg):base(msg)
253         {
254
255         }
256     }
257
258     static public BroadcastException CreateBroadcastSignatureException(string name)
259     {
260         return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", name));
261     }
262
263
264     public class BroadcastException :Exception
265     {
266         public BroadcastException(string msg) :base(msg)
267         {
268
269         }
270     }
271
272     #endregion
273
274 }
275
276
277 /// <summary>
278 /// 消息处理帮助类
279 /// </summary>
280 public sealed class MessengerHelper:MonoBehaviour
281 {
282     private void Awake()
283     {
284         DontDestroyOnLoad(gameObject);
285     }
286
287     public void OnLevelWasLoaded(int unUsed)
288     {
289          //每次新level load 的时候,清理以前的message 。  但有些信息需要保留,通用,所以的话, 就必须有永久信息的存储。list
290         Messenger.CleanUp();
291     }
292
293
294 }

Messenger.cs

时间: 2024-10-18 04:28:31

Unity3D 消息框架设计的相关文章

unity3D 游戏开发之工程代码框架设计思路MVC

unity3D 游戏开发之工程代码框架设计思路MVC 设计目的 1.使工程结构更规范. 2.提高代码可读性,封装性,拓展性 3.提高工作效率. 正文内容: 1.Frame的组成结 (1)视图层(View) (2) 控制层(Control) (3)数据层(Model) 整个Frame是由这三个部分组成,每一层管理属于自己的逻辑,核心思想是游戏逻辑和UI 逻辑独立开.目前遇到的项目工程大多数View和Control逻辑都写在一起,这样后期修改 和维护效率会很低,因为耦合性很高而View又是经常要修改

Linux设备驱动框架设计

引子 Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码.能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Linux内核提供了一套易于扩展和维护的设备驱动框架.Linux内核本身提供一套设备驱动模型,此模型提供了Linux内核对设备的一般性抽象描述,包括设备的电源管理.对象生命周期管理.用户空间呈现等等.在设备模型的帮助下,设备驱动开发工程师从设备的一般性抽象中解脱出来.但是每个设备的具体功能实现还需要大量

niubi-job:一个分布式的任务调度框架设计原理以及实现

niubi-job的框架设计是非常简单实用的一套设计,去掉了很多其它调度框架中,锦上添花但并非必须的组件,例如MQ消息通讯组件(kafka等).它的框架设计核心思想是,让每一个jar包可以相对之间独立的运行,并且由zk辅助进行集群中任务的调度. 接下来,咱们就一步一步的来看下niubi-job整个的框架设计与实现. 框架设计概述 讲解之前,让我们先来看一张niubi-job的框架设计图.如下: 可以看到,该图的结构非常简单,只有四个部分组成. web控制台:负责发布任务,监控任务的状态信息,上传

大型网络游戏服务器的框架设计(一)

服务器是用来处理高并发的请求,同时能够满足扩展的业务逻辑的需求,最重要的是满足三点:并发性,稳定性,扩展性. 经历过两款上线游戏产品,见识到了游戏行业的杂乱无章,虽然和传统软件行业相比,少了那么些规范,但是对个人能力要求还真不比传统软件行业低. 今天开始,陆续利用业余时间将自己设计的一个服务器的框架贴出来,也会包好一些基本的代码,也会用到一些开源库.从最基础的讲起,首先看看一个实时网络游戏服务器的框架: 目前市面上的游戏,总的来说分为两类: 1.弱联网类游戏,像手机上的卡牌类游戏(MT,Dota

[编织消息框架]前言

出书缘由 本项目名叫onequeue意为一流消息队列,参考对象为kafka 虽然最终结果可能达不到一流水准,但那不是主要的,主要是做的心态保持一流的态度 为什么作为kafka参考,又为什么自己重新做? 我在预研kafka发现在发送消息时网络断开会造成消息丢失,而底层没有提供失败回调给开发者使用,在某些场景来讲不允许丢消息的 进一步深入看下源码,虽然某些领域kafka开者人员很熟悉但综合水平觉得不如我,所以产生写消息框架的想法 面向读者 如果你喜欢网络传输,数据存储方向,那么本书会非常适合你,但你

消息队列设计精要【转】

消息队列已经逐渐成为企业IT系统内部通信的核心手段.它具有低耦合.可靠投递.广播.流量控制.最终一致性等一系列功能,成为异步RPC的主要手段之一. 当今市面上有很多主流的消息中间件,如老牌的ActiveMQ.RabbitMQ,炙手可热的Kafka,阿里巴巴自主开发的Notify.MetaQ.RocketMQ等. 本文不会一一介绍这些消息队列的所有特性,而是探讨一下自主开发设计一个消息队列时,你需要思考和设计的重要方面.过程中我们会参考这些成熟消息队列的很多重要思想. 本文首先会阐述什么时候你需要

一种分布式框架设计(二)

本篇主要介绍分布式框架的模块和其主要使用的通信方式zmq. 首先,对于任意的上游结点,它都有可能会把处理的结果发送到任意的一台下游结点中,同时如果下游结点有新增的结点,上游结点还能自动感知并处理.另一方面,任意的下游结点也会要和所有的上游结点保持心跳.如果使用原始的socket,解决上述的问题会比较麻烦,所以我们运用了zmq来解决上述的问题.Zmq具有下述的优点:1. 是一个跨协议的通信方式,目前支持inproc, ipc, tcp, tpic, multicast:2. 具有丰富且功能强大的设

Android 程序框架设计

1.一些概念 模式的定义: 每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心.通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作. 什么是设计模式? 设计模式是在某种特别的情况下,针对某种问题的某种典型.通用的解决方法. 我们是需要适当了解并学习一些设计模式,在程序开发过程中,总是会涉及到一些框架设计,模块设计之类的东西,如果能很好理解并运行设计模式,你所设计的模块或框架将会要稳定得多,因为这些设计模式它们都是通用的解决方案,是经过实践经验

Socket开发框架之框架设计及分析

虽然在APP应用.Web应用.Winform应用等大趋势下,越来越多的企业趋向于这些应用系统开发,但是Socket的应用在某些场合是很必要的,如一些停车场终端设备的接入,农业或者水利.压力监测方面的设备数据采集等,以及常见的IM(即时通讯,如腾讯QQ.阿里旺旺等)的客户端,都可以采用Socket框架进行相关的数据采集和信息通讯用途的,Socket应用可以做为APP应用.Web应用和Winform应用的补充. 1.Socket应用场景 一般情况下,客户端和服务端进行Socket连接,需要进行数据的