Unity自带消息系统,如SendMessage等,此方法利用的反射,且会反射游戏物体上的所有组件,对性能不友好。而且由于参数为方法名称,所以如果使用代码混淆,则会无法调用 方法,且难以追踪问题。一般消息发送采用事件或者委托进行。但是对于一些跨线程操作,或者涉及系统底层(一般也不再主线程)消息时也会更新UI(确切的说是渲染问题)错误。所以在此基于脚本update方法,形成一个统一的消息机制。
主要有三部分组成,消息中心、消息处理器和消息通道。
此部分主要用来管理消息,包括存储、注册等。
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;
}
}
}
此部分功能负责发送消息。
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();
}
}
}
消息通道为枚举类型,决定了此消息通过什么通道发送,可自行添加。并通过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;
}
}
}
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
GameObject.DontDestroyOnLoad(messageProcessor);来实现不被销毁
最初方案如下:
#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?
1、要想MessageProcessor一直存在,则需要当其为空时重新生成,但在关闭应用时报错。
2、不重新生成MessageProcessor,场景加载时杀掉MessageProcessor,则消息无法传递。
3、将messageProcessor独立出来,不在MessageCenter中生成。并加DontDestroyOnLoad方法。此时如果场景重新加载则会有两个MessageProcessor。
虽然存在一些矛盾,但是可以通过场景加载时采用Additive方式,或者其他方法,找到适合自己工程的处理手段。
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);
}
}
}