为了降低耦和,Unity自带了消息机制。主要体现在如下三个方法:
SendMessage
, SendMessageUpwards
, BroadcastMessage
但是我们平时几乎不会使用它们,主要有如下几点缺陷:
那么现在可以针对这些缺陷去实现自己的消息处理框架了,基于C#的委托去实现,性能上和安全性上都远远强过Unity的原生消息传递机制。
一般来讲我不喜欢使用单例模式实现除非逼不得已,因为不容易控制单例的生存周期,很难有一个合理的初始化顺序。同时为了兼顾易用性和安全性,所以我们使用泛型静态类来实现
首先写一个异常,抛出该框架内部的异常
public class MessageException : Exception {
public MessageException(string message, Exception innerException) : base(message, innerException) { }
public MessageException(string message):base(message) { }
public MessageException() : base() { }
}
接下来是框架部分
namespace ZCC.MessageCenter {
/// 消息中心的异常
public class MessageException : Exception {
public MessageException(string message, Exception innerException) : base(message, innerException) { }
public MessageException(string message):base(message) { }
public MessageException() : base() { }
}
/// 消息中心
/// 消息枚举 每种类型的消息枚举都有一个消息中心
public static class MessageCenter<TMessage> where TMessage: struct {
/// 已注册的消息字典
private static Dictionary<TMessage, EventHandler> _dicMessages = new Dictionary<TMessage, EventHandler>();
/// 向某消息注册监听者
public static void AddListener(TMessage message, EventHandler messageHandler) {
if (!_dicMessages.ContainsKey(message))
_dicMessages.Add(message, null);
_dicMessages[message] += messageHandler;
}
/// 注销某消息的某个监听者
public static void RemoveListener(TMessage message, EventHandler messageHandler) {
if (!_dicMessages.ContainsKey(message)) {
Debug.LogError(typeof(TMessage).Name + ":" + "[RemoveListener]不存在此messageKey:[" + message.ToString() + "]");
return;
}
_dicMessages[message] -= messageHandler;
}
/// 发送消息给所有监听者
public static void SendMessage(TMessage message, object sender, EventArgs eventArgs) {
if (!_dicMessages.ContainsKey(message)) {
Debug.LogError(typeof(TMessage).Name+":"+"[SendMessage]不存在此messageKey:["+ message.ToString()+"]");
return;
}
Delegate[] invocationList = _dicMessages[message].GetInvocationList();
foreach (Delegate invocation in invocationList) {
try {
(invocation as EventHandler)(sender, eventArgs);
}
catch(Exception e) {
Debug.LogError(e);
throw e;
}
}
}
}
}
TMessage作为泛型参数,被限定为值类型,使用枚举作为一类消息的标识,代码看起来可读性很高。不同的类型参数有自己的消息字典,维护各自内部的消息处理。 比如为了传递UI消息,我们先设计一个UIMessage枚举,用来枚举所有UI消息
public enum UIMessage {
/// 空
ENull = 0,
/// 打开窗口
OpenForm = 1,
/// 关闭窗口
CloseForm = 2
}
然后针对自己的业务逻辑设计传递的消息类,该类需要继承自EventArgs
public class UIEventArgs: EventArgs{
public string FormName;
public UIEventArgs(string formName){
this.FormName = formName;
}
}
具体用法也不必赘述了, 下面写一个小例子
public class Example: MonoBehaviour {
private void Awake( ){
//向OpenForm注册监听者
MessageCenter<UIMessage>.AddListener(UIMessage.OpenForm, OnOpenForm);
//向所有注册OpenForm的监听者发送消息
MessageCenter<UIMessage>.SendMessage(UIMessage.OpenForm,new UIEventArgs("LoginForm"));
}
private void OnOpenForm(object sender, this, EventArgs eventArgs){
print("打开窗口"+(eventArgs as UIEventArgs).FormName);
}
}
当然还有一些地方可以扩展改进,欢迎留言!