title: 消息机制
date: 2022-11-21 22:25:57
tags#
这篇文章希望能够教会你们一种构建简单Unity框架,使Unity的各个模块能够通信,特别是用在UI上的通信,在实现代码高度解耦的情况下,使不同的UIPanel,之间能够互相调用通信,即和其他模块联动
消息机制的大致类图
AreaCode
通知各个模块的消息码
/// <summary>
/// UI模块
/// </summary>
public const int UI = 0;
/// <summary>
/// GAME模块
/// </summary>
public const int GAME = 1;
/// <summary>
/// CHARACTER模块
/// </summary>
public const int CHARACTER = 2;
/// <summary>
/// NET模块
/// </summary>
public const int NET = 3;
/// <summary>
/// AUDIO模块
/// </summary>
public const int AUDIO = 4;
/// <summary>
/// SCENE模块
/// </summary>
public const int SCENE = 5;
MonoBase
MonoBase 脚本用来拓展 MonoBehaviour ,使每一个继承MonoBase 的脚本都能有MonoBase中的方法
public class MonoBase : MonoBehaviour
{
/// <summary>
/// 定义一个虚方法
/// </summary>
public virtual void Execute(int eventCode, object message)
{
}
}
ManagerBase(重点)
用来绑定和解绑消息码,继承MonoBase , 也是其他模块 Manager脚本的父类
ManagerBase 具有一个字典用来存放 消息码 和 绑定了此消息的脚本
private Dictionary<int,List<MonoBase>> dict = new Dictionary<int, List<MonoBase>>();
要怎么才能让消息码绑定对应的脚本
我们定义一个 add 方法 用来绑定消息
// 为什么存放脚本的类型是 MonoBase 因为 消息码会有很多给,要定义不同的脚本,此时我们需要一个父类来统一这些脚本
public void Add(int eventCode , MonoBase mono)
{
// 存放脚本的链表
List<MonoBase> list = null;
// 先判断这个消息码有没有绑定过脚本
if(dict.ContainsKey(eventCode))
{
list = new List<MonoBase>();
list.add(mono);
dict.add(eventCode,mono);
// 此时就形成了一个消息码和与之对应脚本列表的绑定,在后面我们要通知这些脚本时,只需要遍历这些脚本
}
// 如果之前已经有脚本绑定了这个消息码
else{
// 从字典中取出这个链表,把这个脚本加到这个链表中去
list = dict[eventCode];
list.add(mono);
}
}
// 定义一个Add的重载方法,用来与模块那边匹配
public void Add(int[] eventCode,MonoBase mono)
{
for(int i = 0; i< eventCode.Length; i++)
{
add(eventCode[i],mono);
}
}
定义解绑消息的方法 Remove
// 和 Add 方法类似
public void Remove(int eventCode, MonoBase mono)
{
//没注册过 没法移除 报个警告
if (!dict.ContainsKey(eventCode))
{
Debug.LogWarning("没有这个事件" + eventCode + "注册");
return;
}
List<MonoBase> list = dict[eventCode];
if (list.Count == 1)
dict.Remove(eventCode);
else
list.Remove(mono);
}
public void Remove(int[] eventCodes, MonoBase mono)
{
for (int i = 0; i < eventCodes.Length; i++)
{
Remove(eventCodes[i], mono);
}
}
脚本执行 消息码的方法
// MonoBase 让这些脚本都一个同名的执行方法
public virtual void Execute(int eventCode, object message)
{
// 没有脚本绑定过这个消息码
if(!dict.ContainsKey[eventCode])
return;
else
{
// 字典取出 绑定了脚本
list<MonoBase> list = dict[eventCode];
// for 循环 依次执行
for(int i = 0; i< list.Count ; i++)
{
// 调用脚本自身的Execute方法来执行具体方法
list[i].Execute(eventCode,message);
}
}
}
MsgCenter
将各个模块联系起来转发消息
// 制造单例模式
public static MsgCenter Instance;
privare void Awake()
{
gameObject.AddComponent<>();
}
// 消息转发中心,具体每个模块只要调用这个方法,传入areaCode 等参数就可以将消息通知到其他模块
private void Dispatch(int areaCode , int eventCode, object message)
{
switch(areaCode)
{
case AreaCode.UI:
UIManager.Instance.Execute(eventCode,message); // UIManager 有什么用会在后面的UI模块里面讲解
break;
case AreaCode.Scenes:
ScenesManager.Instance.Execute(eventCode,message);
break;
// 还有很多模块 ····
}
}
这样我们就写完这个框架的主体内容,剩下的就是编写各个模块对应 Manager , 用每个模块的Manager脚本来维护 各个模块的信息
UI 模块
UIManager
*UIManager 继承ManagerBase有 ManagerBase 的绑定,解绑,执行方法,UIManager 在调用这些方法时也维护了自身的字典来保存脚本信息,这样就起到了区分各个模块的作用
public static UIManager Instance;
// 构造单例,因为保存脚本的字典只能是唯一
public void Awak()
{
Instance = this;
}
UIBase
UIBase 是UI模块具体类的父类,通过调用UIManager 和 MsgCenter这个单例来实现消息的绑定和转发
UIBase 继承 MonoBase
// 脚本消息的链表,因为会有很多UI脚本继承UIBase 他们绑定的消息码最终都会发到CodeList里面
public List<int> CodeList = new List<int>CodeList;
public void Bind(int[] eventCode)
{
CodeList.addRangeeventCode);
// 单例调用UI模块的Manager 来把消息码和对应的脚本发给 ManagerBase
UIManager.Instance.Add(CodeList.ToArray(),this);
}
public void UnBind()
{
UIManager.Instance.Remove(CodeList.ToArray(), this);
CodeList.Clear();
}
// OnDestroy 函数在游戏结束时自动调用用来接触绑定
public virtual void OnDestroy()
{
if (list != null)
UnBind();
}
// 发送消息
public void Dispatch(int areaCode, int eventCode, object message)
{
MsgCenter.Instance.Dispatch(areaCode, eventCode, message);
}
总结
这个消息框架,主要就是将游戏分为各个模块,将每个模块想象成一个单独的城市,每个城市有一个Manager 如 UIManager , 消息码 相当于地址来区别 正真的消息 object message 发往何处。MsgCents 则相当于一个中转站,识别 消息码 发往何处。