Unity系统消息广播

文章目录

  • 1.前言
  • 2.消息系统组成
    • 2.1 消息中心
    • 2.2 消息处理器
    • 2.3 消息通道
    • 2.4 问题标记
      • 2.4.1 MessageProcessor可能会被销毁
      • 2.4.2 最初方案引发的问题
      • 2.4.3 矛盾问题
  • 3.调用方法

1.前言

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

2.消息系统组成

主要有三部分组成,消息中心、消息处理器和消息通道。

2.1 消息中心

此部分主要用来管理消息,包括存储、注册等。

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 消息处理器

此部分功能负责发送消息。

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类及其子类传递参数

using UnityEngine;

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

    /// 
    /// Base Message class imitating android Message class.
    /// arg1、arg2 and message are used to store simple values.
    /// 
    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() { }
    }

    /// 
    /// Extend Message class and carry more infomation
    /// 
    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的单例采用如下,是出于以下几点考虑。

        #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 最初方案引发的问题

最初方案如下:

        #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();
                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.调用方法

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);
        }
    }
}

你可能感兴趣的:(Unity简单功能封装)