Unity | 工具类:消息管理器-延迟分发
目录
一、消息管理器
二、获得新装备
三、UI面板创建
消息管理器除了简单的订阅(Subscribe)、取消订阅(Unsubscribe)操作以外,还需处理延迟分发(Dispatch)的情况。
(即时处理可参考:Unity | 工具类-利用事件系统进行业务串通-CSDN博客)
假设玩家在游戏中获得新装备后,系统则会发送消息通知UI面板去显示第二个页签上的红点提示,但此时UI面板尚未创建,当玩家打开UI面板时消息早就发送过了。而延迟消息可以先把消息推送到缓存中,由需要拉取延迟消息的类(UI面板)自行调用拉取函数即可。这样的设计可以应对大部分游戏对于消息管理方面的需求,包括刷怪、关卡的消息提示等。
一、消息管理器
public class MessageManager
{
static MessageManager mInstance;
public static MessageManager Instance
{
get
{
return mInstance ?? (mInstance = new MessageManager());
}
}
Dictionary<string, Action<object[]>> mMessageDict = new Dictionary<string, Action<object[]>>(32);
// 将缓存字典设置为存储队列,防止多次分发时覆盖,key为消息名,value为参数队列
Dictionary<string, Queue<object[]>> mDispatchCacheDict = new Dictionary<string, Queue<object[]>>(16);
private MessageManager() { }
//订阅消息
public void Subscribe(string message, Action<object[]> action)
{
Action<object[]> value = null;
//已有消息则追加绑定
if (mMessageDict.TryGetValue(message, out value))
{
value += action;
mMessageDict[message] = value;
}
else //没有消息则添加到字典里
{
mMessageDict.Add(message, action);
}
}
//取消消息订阅
public void Unsubscribe(string message)
{
mMessageDict.Remove(message);
}
//允许按委托实例取消订阅
public void Unsubscribe(string message, Action<object[]> action)
{
if (mMessageDict.TryGetValue(message, out var existingAction))
{
existingAction -= action; // 移除特定委托
if (existingAction == null)
{
mMessageDict.Remove(message); // 无订阅者则移除消息
}
else
{
mMessageDict[message] = existingAction;
}
}
}
public void Dispatch(string message, object[] args = null, bool addToCache = false)
{
if (addToCache)
{
// 如果不存在则创建新队列
if (!mDispatchCacheDict.TryGetValue(message, out var queue))
{
queue = new Queue<object[]>();
mDispatchCacheDict[message] = queue;
}
queue.Enqueue(args); // 参数入队
}
else
{
// 触发所有订阅者
if (mMessageDict.TryGetValue(message, out var action))
{
action.Invoke(args);
}
}
}
public void ProcessDispatchCache(string message)
{
if (mDispatchCacheDict.TryGetValue(message, out var queue))
{
// 处理队列中的所有消息
while (queue.Count > 0)
{
var args = queue.Dequeue();
Dispatch(message, args); // 分发时不再缓存
}
mDispatchCacheDict.Remove(message);
}
}
/// <summary>
/// 重置消息管理器,必须用Static方法修饰
/// </summary>
public static void Reset()
{
mInstance = null; // 下次访问时会重新初始化
}
}
二、获得新装备
获得新食物时,UI面板未初始化。
//处理获得新食物的消息
void OnGetNewFood1()
{
MessageManager.Instance.Dispatch("GetNewFood", new object[] { 3 }, true);
}
void OnGetNewFood2()
{
MessageManager.Instance.Dispatch("GetNewFood", new object[] { 2 }, true);
}
三、UI面板创建
void OnEnable()
{
MessageManager.Instance.Subscribe("GetNewFood", OnGetNewFood);
//处理延迟消息
MessageManager.Instance.ProcessDispatchCache("GetNewFood");
//避免场景切换后残留旧数据,可重置单例实例。
SceneManager.sceneLoaded += (scene, mode) => MessageManager.Reset();
}
/// <summary>
/// 移除所有订阅
/// </summary>
// void OnDisable()
// {
// MessageManager.Instance.Unsubscribe("GetNewFood");
// }
/// <summary>
/// 移除当前实例的订阅
/// </summary>
void OnDisable()
{
// 仅移除当前实例的委托
MessageManager.Instance.Unsubscribe("GetNewFood", OnGetNewFood);
}
private void OnGetNewFood(object[] obj)
{
if (obj != null && obj.Length > 0 && obj[0] is int count)
{
Debug.Log($"获得 {count} 个新食物");
}
else
{
Debug.LogError("无效的食物数量参数!");
}
}