Unity框架设计(一) 消息处理框架
关于Unity的原生消息机制
为了降低耦和,Unity自带了消息机制。主要体现在如下三个方法:
SendMessage
, SendMessageUpwards
, BroadcastMessage
但是我们平时几乎不会使用它们,主要有如下几点缺陷:
1. 内部使用反射,性能较差
2. 严重依赖字符串,无法在编译阶段实现类型安全
3. 只有继承自MonoBehaviour才可以调用
4. 可以调用私有方法,破坏封装性
所以开发游戏过程中构建一套自己的消息处理机制是必不可少的
实现自己的消息处理框架
那么现在可以针对这些缺陷去实现自己的消息处理框架了,基于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 {
/// <summary>消息中心的异常</summary>
public class MessageException : Exception {
public MessageException(string message, Exception innerException) : base(message, innerException) { }
public MessageException(string message):base(message) { }
public MessageException() : base() { }
}
/// <summary>消息中心</summary>
/// <typeparam name="TMessage">消息枚举 每种类型的消息枚举都有一个消息中心</typeparam>
public static class MessageCenter<TMessage> where TMessage: struct {
/// <summary>已注册的消息字典</summary>
private static Dictionary<TMessage, EventHandler> _dicMessages = new Dictionary<TMessage, EventHandler>();
/// <summary>向某消息注册监听者 </summary>
public static void AddListener(TMessage message, EventHandler messageHandler) {
if (!_dicMessages.ContainsKey(message))
_dicMessages.Add(message, null);
_dicMessages[message] += messageHandler;
}
/// <summary>注销某消息的某个监听者 </summary>
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;
}
/// <summary>发送消息给所有监听者</summary>
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 {
/// <summary>空</summary>
ENull = 0,
/// <summary>打开窗口<summary>
OpenForm = 1,
/// <summary>关闭窗口<summary>
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);
}
}
当然还有一些地方可以扩展改进,欢迎留言!