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

```


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM