zoukankan      html  css  js  c++  java
  • Unity系统消息广播

    # 1.前言
    Unity自带消息系统,如SendMessage等,此方法利用的反射,且会反射游戏物体上的所有组件,对性能不友好。而且由于参数为方法名称,所以如果使用代码混淆,则会无法调用 方法,且难以追踪问题。一般消息发送采用事件或者委托进行。但是对于一些跨线程操作,或者涉及系统底层(一般也不再主线程)消息时也会更新UI(确切的说是渲染问题)错误。所以在此基于脚本update方法,形成一个统一的消息机制。

    # 2.消息系统组成
    主要有三部分组成,消息中心、消息处理器和消息通道。
    ## 2.1 消息中心
    此部分主要用来管理消息,包括存储、注册等。

    ```csharp
    using System;
    using System.Collections.Generic;
    using UnityEngine;

    namespace MSG
    {
    public delegate void MessageHandler(Message message);

    public class MessageCenter
    {
    #region Instance
    private static MessageCenter instance;
    private static MessageProcessor messageProcessor;
    private static bool instanceCreated = false;

    public static MessageCenter GetInstance()
    {
    if (!instanceCreated)
    {
    instance = new MessageCenter();

    GameObject processor = new GameObject("MessageProcessor");
    messageProcessor = processor.AddComponent<MessageProcessor>();
    instanceCreated = true;
    }

    return instance;
    }

    private MessageCenter() { }
    #endregion

    private Dictionary<MsgChannel, MessageHandler> messageHandlers = new Dictionary<MsgChannel, MessageHandler>();
    private Queue<Message> messageQueue = new Queue<Message>();

    public void BroadcastMessage(Message message)
    {
    messageQueue.Enqueue(message);
    }

    public Message PostMessage()
    {
    Message message = null;

    if (messageQueue.Count != 0)
    {
    message = messageQueue.Dequeue();
    }
    return message;
    }

    public void RegisterMessageHandler(MsgChannel type,MessageHandler messageHandler)
    {
    MessageHandler handler = null;
    bool exist = messageHandlers.TryGetValue(type, out handler);

    if (!exist)
    {
    Debug.Log("Add message handler " + messageHandler.Method.Name);
    messageHandlers.Add(type, messageHandler);
    }
    else
    {
    //如果已经注册,则不会重复注册
    Delegate[] handlers = handler.GetInvocationList();
    if (Array.IndexOf(handlers, messageHandler) == -1)
    {
    Debug.Log("Plus message handler " + messageHandler.Method.Name);
    handler += messageHandler;
    messageHandlers[type] = handler;
    }
    }
    }

    public void UnregisterMessageHandler(MsgChannel type,MessageHandler messageHandler)
    {
    MessageHandler handler;
    bool exist = messageHandlers.TryGetValue(type, out handler);

    if (exist)
    {
    handler -= messageHandler;

    if (handler == null)
    {
    Debug.Log("Message handler to be unregistered is null now" + messageHandler.Method.Name);
    messageHandlers.Remove(type);
    }
    else
    {
    Debug.Log("Message handler to be unregistered is unregistered " + messageHandler.Method.Name);
    messageHandlers[type] = handler;
    }
    }
    }

    public MessageHandler GetMessageHandler(MsgChannel type)
    {
    MessageHandler handler;
    messageHandlers.TryGetValue(type, out handler);

    return handler;
    }
    }
    }
    ```
    ## 2.2 消息处理器
    此部分功能负责发送消息。

    ```csharp
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    namespace MSG
    {
    public class MessageProcessor : MonoBehaviour
    {
    public bool ProcessorActive = true;

    private void ProcessMessages()
    {
    Message message = MessageCenter.GetInstance().PostMessage();

    if (message != null)
    {
    MessageHandler handler = MessageCenter.GetInstance().GetMessageHandler(message.what);

    if (handler != null)
    {
    handler(message);
    }
    }
    }

    private void Update()
    {
    ProcessMessages();
    }
    }
    }


    ```
    ## 2.3 消息通道
    消息通道为枚举类型,决定了此消息通过什么通道发送,可自行添加。并通过Message类及其子类传递参数

    ```csharp
    using UnityEngine;

    namespace MSG
    {
    public enum MsgChannel
    {
    NONE = 0,
    TEST_MSG1 = 1,
    TEST_MSG2 = 2
    }

    /// <summary>
    /// Base Message class imitating android Message class.
    /// arg1、arg2 and message are used to store simple values.
    /// </summary>
    public class Message
    {
    public MsgChannel what = MsgChannel.NONE;
    public int arg1 = 0;
    public int arg2 = 0;
    public string message;

    public Message(MsgChannel what, int arg1 = 0, int arg2 = 0, string message = "")
    {
    this.what = what;
    this.arg1 = arg1;
    this.arg2 = arg2;
    this.message = message;
    }

    public Message() { }
    }

    /// <summary>
    /// Extend Message class and carry more infomation
    /// </summary>
    public class NetworkMessage : Message
    {
    public Texture2D t2d;

    public NetworkMessage(MsgChannel what,Texture2D t2d, int arg1 = 0, int arg2 = 0, string message = "")
    :base(what,arg1,arg2,message)
    {
    this.t2d = t2d;
    }
    }
    }


    ```
    ## 2.4 问题标记
    MessageCenter的单例采用如下,是出于以下几点考虑。

    ```csharp
    #region Instance
    private static MessageCenter instance;
    private static MessageProcessor messageProcessor;
    private static bool instanceCreated = false;

    public static MessageCenter GetInstance()
    {
    if (!instanceCreated)
    {
    instance = new MessageCenter();

    GameObject processor = new GameObject("MessageProcessor");
    messageProcessor = processor.AddComponent<MessageProcessor>();
    GameObject.DontDestroyOnLoad(messageProcessor);
    instanceCreated = true;
    }

    return instance;
    }

    private MessageCenter() { }
    #endregion
    ```
    ### 2.4.1 MessageProcessor可能会被销毁
    GameObject.DontDestroyOnLoad(messageProcessor);来实现不被销毁
    ### 2.4.2 最初方案引发的问题
    最初方案如下:

    ```csharp
    #region Instance
    private static MessageCenter instance;
    private static MessageProcessor messageProcessor;

    private static bool instanceCreated = false;

    public static MessageCenter GetInstance()
    {
    if (!instanceCreated)
    {
    instance = new MessageCenter();

    //GameObject processor = new GameObject("MessageProcessor");
    //messageProcessor = processor.AddComponent<MessageProcessor>();
    instanceCreated = true;
    }

    if (messageProcessor == null)
    {
    GameObject processor = new GameObject("MessageProcessor");
    messageProcessor = processor.AddComponent<MessageProcessor>();
    }

    return instance;
    }

    private MessageCenter() { }
    #endregion
    ```
    最初方案中instance和messageProcessor是单独处理的,但是在使用过程中,在OnDisable方法中调用了MessageCenter的单例(如3.调用方法)。则当unity关闭,随机销毁对象时,如果MessageCenter先销毁,在销毁MessageProcessor时,则会在OnDestroy方法中先调用OnDisable方法。此时会重新生成MessageCenter的单例,此时就会报如下错误:
    **Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?**

    ### 2.4.3 矛盾问题
    1、要想MessageProcessor一直存在,则需要当其为空时重新生成,但在关闭应用时报错。
    2、不重新生成MessageProcessor,场景加载时杀掉MessageProcessor,则消息无法传递。
    3、将messageProcessor独立出来,不在MessageCenter中生成。并加DontDestroyOnLoad方法。此时如果场景重新加载则会有两个MessageProcessor。

    **虽然存在一些矛盾,但是可以通过场景加载时采用Additive方式,或者其他方法,找到适合自己工程的处理手段。**

    # 3.调用方法

    ```csharp
    using MSG;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;

    public class MessageSystemTest : MonoBehaviour
    {
    public Button button;

    private void Start()
    {
    button.onClick.AddListener(() =>
    {
    MessageCenter.GetInstance().BroadcastMessage(new Message(MsgChannel.TEST_MSG1, 111));
    MessageCenter.GetInstance().BroadcastMessage(new Message(MsgChannel.TEST_MSG1, 111));
    MessageCenter.GetInstance().BroadcastMessage(new NetworkMessage(MsgChannel.TEST_MSG2, null));
    });
    }

    void OnEnable ()
    {
    MessageCenter.GetInstance().RegisterMessageHandler(MsgChannel.TEST_MSG1, Method);
    MessageCenter.GetInstance().RegisterMessageHandler(MsgChannel.TEST_MSG1, Method1);
    MessageCenter.GetInstance().RegisterMessageHandler(MsgChannel.TEST_MSG2, Method2);
    }

    private void OnDisable()
    {
    MessageCenter.GetInstance().UnregisterMessageHandler(MsgChannel.TEST_MSG1, Method);
    MessageCenter.GetInstance().UnregisterMessageHandler(MsgChannel.TEST_MSG1, Method1);
    MessageCenter.GetInstance().UnregisterMessageHandler(MsgChannel.TEST_MSG2, Method2);
    }

    void Method(Message message)
    {
    Debug.LogFormat("Method : Message -{0}- obtained from channel {1}", message.arg1, message.what);
    }

    void Method1(Message message)
    {
    Debug.LogFormat("Method1 : Message -{0}- obtained from channel {1}", message.arg1, message.what);
    }

    void Method2(Message message)
    {
    NetworkMessage networkMessage = message as NetworkMessage;

    if(networkMessage != null)
    {
    Debug.LogFormat("NetworkMessage obtained from channel {0}", message.what);
    }
    }
    }

    ```

  • 相关阅读:
    伪类和伪元素的区别
    inline-block 空隙
    递归模版,绑定树
    lessJs
    怎样开发单页面app
    控件的开发接口的设计原理
    加载器中的预编
    linux加固脚本
    ogg同步服务配置复制和同步进程的开始文件及RBA
    tidb4.0修改参数全过程
  • 原文地址:https://www.cnblogs.com/llstart-new0201/p/11764944.html
Copyright © 2011-2022 走看看