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);
        }
    }
}
```
 
                    
                 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号