当前位置: 首页 > news >正文

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("无效的食物数量参数!");
        }
    }

相关文章:

  • CSS3 用户界面设计指南
  • Vue3计算属性深度解析:经典场景与Vue2对比
  • 「JavaScript深入」Web Components:构建可重用的跨框架组件
  • 【图片合并PDF】多个文件夹里的图片合并PDF,一次性批量合并多个文件夹里的图片转成PDF,基于WPF完成方案分享
  • Netty启动源码NioEventLoop剖析accept剖析read剖析write剖析
  • windows系统,pycharm运行.sh文件
  • 【已解决】电脑空间告急?我的 Ollama、Docker Desktop软件卸载清理全记录
  • environment.resolvePlaceholders占位符解析优化
  • 分布式事务中XA 事务 和 两阶段提交(2PC)应该如何理解?
  • 自定义日志回调函数实现第三方库日志集成:从理论到实战
  • 前端面试:px 如何转为 rem
  • mysql select distinct 和 group by 哪个效率高
  • 单一责任原则在Java设计模式中的深度解析
  • 完全二叉树节点的数量 平衡二叉树
  • 【视频】SRS将RTMP转WebRTC、HLS流;获取RTSP转其它流
  • JavaScript 运算符详解
  • 关于stac和clac的进一步细节及EFLAGS
  • 蓝桥备赛(18)- 红黑树和 set 与 map(上)
  • 每日一题力扣2697.字典序最小回文串c++
  • (每日一题) 力扣 179 最大数
  • 加强公司网站建设/百家号权重查询
  • 做网站系统开发的意义/企业网页设计与推广
  • 深圳手机网站开发/网站推广软件免费
  • 直播网站开发费用/自助建站系统开发
  • 晨光科技+网站建设/深圳新闻最新事件
  • 峨山网站建设/谷歌play商店