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);
}
}
}
```
Unity系统消息广播的更多相关文章
- Windows API 函数列表 附帮助手册
所有Windows API函数列表,为了方便查询,也为了大家查找,所以整理一下贡献出来了. 帮助手册:700多个Windows API的函数手册 免费下载 API之网络函数 API之消息函数 API之 ...
- 【java回调】java两个类之间的回调函数传递
背景交代:熟悉用js开发的cordovaAPP:对java一窍不通的我,老师让做一个监测用户拍照事件的功能,无奈没有找到现成的库,无奈自己动手开发java插件~~0基础java GreenHand,祝 ...
- API函数
1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAddConnection3 创建同 ...
- WINDOWS API 函数(超长,值得学习)
一.隐藏和显示光标 函数: int ShowCursor ( BOOL bShow ); 参数 bshow,为布尔型,bShow的值为False时隐藏光标,为True时显示光标:该函数的返回值为整型 ...
- linux API函数大全
获取当前执行路径:getcwd1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAdd ...
- Windows API Finishing
input { font-size: 14px; height: 26px } td { border-style: none; border-color: inherit; border-width ...
- Windows API函数大全(精心总结)
WindowsAPI函数大全(精心总结) 目录 1. API之网络函数... 1 2. API之消息函数... 1 3. API之文件处理函数... 2 4. API之打印函数... 5 5. ...
- SCADA 必备函数之 :关于消息的函数
Message Functions BroadcastSystemMessage//是将一条系统消息广播给系统中所有的顶级窗口. BroadcastSystemMessageEx//将消息发送到指定的 ...
- WINDOWS-API:API函数大全
操作系统除了协调应用程序的执行.内存分配.系统资源管理外,同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务是一个函数),可以帮肋应用程序达到开启视窗.描绘图形.使用周边设备的目的,由 ...
随机推荐
- 1.html基础知识
1.html定义: html是一种超文本标记语言,“超文本”是指页面可以包含图片.链接.音乐.程序等非文字元素. Html不是一种编程语言. 2.html5的新特性: 用于绘画的canvas元素: 用 ...
- 序列标注(HMM/CRF)
目录 简介 隐马尔可夫模型(HMM) 条件随机场(CRF) 马尔可夫随机场 条件随机场 条件随机场的特征函数 CRF与HMM的对比 维特比算法(Viterbi) 简介 序列标注(Sequence Ta ...
- UVA12983 The Battle of Chibi
第一眼能看出来是个dp O($n^3$) 暴力应该很好想 dp[i][j] = $\sum_{k=1}^i [a[k] < a[i]] *dp[k][j-1]$ 发现dp[i][j] 为前面小于 ...
- 数据库系统概论——从E-R模型到关系模型
E-R模型和关系模型都是现实世界抽象的逻辑表示 E-R模型并不被 DBMS直接支持,更适合对现实世界建模 关系模型是 DBMS直接支持的数据模型 基本 E-R图中的元素包括实体集.联系集.属性 椭圆框 ...
- Python sys.setdefaultencoding('utf-8') 后就没输出
为了解决Python的 UnicodeDecodeError: 'ascii' codec can't decode byte ,我们可以加入以下代码. import sys reload(sys) ...
- CSS技巧 (2) · 多列等高布局
前言 最近,面试的时候都碰到一些关于利用CSS实现多列等高布局或者一侧宽度固定,另一侧宽度自适应的问题,下面稍微总结一下: 先看一道题目 巧妙的多列等高布局 规定下面的布局,实现多列等高布局,要求两 ...
- 值类型不允许赋值为Null
与引用类型不同,值类型不可能包含 null 值. 每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值.有关值类型默认值的信息,请参见默认值表. bool false byte 0 char ' ...
- Mongoose: aggregate聚合 $group使用说明
aggregate聚合是通过管道操作实现的.聚合管道里的每一步输出,都会作为下一步的输入,每一步在输入文档执行完操作后生成输出文档. 聚合管道: $project 修改输入文档的结构.可以用来重命名 ...
- 使用echarts画一个类似组织结构图的图表
昨天,写了一篇关于圆环进度条的博客(请移步:Vue/React圆环进度条),已经烦不胜烦,今天又遇到了需要展示类似公司的组织结构图的功能需求,要冒了!!! 这种需求,自己用div+css也是可以实现的 ...
- 利用procedure批量插入数据
正文 要求在页面查询到5000条数据,为了方便插入,准备用shell脚本写curl命令调用自己写的代码接口,但是速度慢,而且写的时候遇到点儿小问题,故用sql语句写了这个功能 由于operat ...