引子
在开发过程中,有时会遇到一些难以解决的情景。 如UGUI中更改语言设置时,如果有多级的页面,则需要同时修改所有显示着的页面的文字。 在这种特定的情况中,要么预先设定好会该情景下需要修改的页面并进行修改,要么遍历Canvas下的所有文字组件。这两种方法虽然都能解决这个问题,但是都存在着效率太差或是不易扩展的问题。 而消息机制可以通过频道的方式精准地通知到挂载了接受器的当前显示的页面,具有安全快捷的优势。
简而言之,消息机制最突出的优势,就是可以无需通过查找物体而实现一对多的联系。
原理
以类似收音机的频道来接收和发布信息,以定义一个字典类来存取频道和频道中的所有订阅器。
消息发布 一则信息包含有频道和泛型这两个参数。 当一个脚本发布信息时,会通知到频道中的所有类型相符的订阅器,订阅器接收到信息后,可以读入信息并执行预设的委托。
消息接收 脚本添加一个泛型类用作接收发送消息的主体,这一步骤一般在生命周期中的Awake()或Start()完成。 该主体有两个参数,一个是消息订阅的频道,一个是泛型。 该消息主体在生成时会生成一个订阅器,并将其加入到对应的频道中实时监听。
实际脚本中的应用
首先来看一下在实际脚本中的应用。
消息接收 在脚本的Start()中挂载消息接收器。 读取"Test"频道中的String类型消息,并将其打印在Console窗口。
TestTrigger = Messenger.Default.Subscribe("Test", (string call) =>
{
Debug.Log(call);
});
消息发送 在Test频道中发送 string类型的值“Hello”
Messenger.Default.Publish("Test","Hello");
此时的Console窗口会打印出 Hello
简单的消息传递系统搭建
下面来开始搭建一个简单的消息传递系统。
首先,我们先创建一个MessageObj.cs脚本,作为消息的主体,其中应该实现发布和接收两个方法。
public class MessageObj<T>
{
public void Publish(Object message)
{
//todo:消息发布
}
public void Subscribe(Action<T> action)
{
//todo:消息接收
}
}
而当物体挂载上一个消息接受器时,这个物体会新建一个订阅器,该订阅器加入接受器中的频道,订阅该频道的所有消息。 所以先定义一个订阅器。 包含所属的消息主体,委托,用于调用的线程上下文,及用作标识的Key。
public class Subscription
{
private MessageObj<T> messageObj;
private Action<T> action;
private SynchronizationContext context;
public string Key { get; private set; }
public Subscription(MessageObj<T> messageObj, Action<T> action)
{
this.messageObj = messageObj;
this.Key = Guid.NewGuid().ToString();
this.action = action;
this.messageObj.Add(this);
}
}
其中的构造函数会将该订阅器加入消息主体。
void Add(Subscription subscription)
{
_subscriptions.Add(subscription.Key,subscription);
}
此时的消息接收函数就可以创建一个订阅器并返回了。
public Subscription Subscribe(Action<T> action)
{
return new Subscription(this, action);
}
然后定义一个字典<标识符,订阅器>来存储该消息主体中的所有订阅器。
private Dictionary<string, Subscription> _subscriptions =new Dictionary<string, Subscription>();
此时一个可以接收信息和发送信息的订阅器就完成了。 来做一个场景试一下能不能用。 在空场景中新建一个空物体,挂上一个用于测试的新脚本。
public class Test : MonoBehaviour
{
//定义并初始化一个消息主体
private MessageObj<string> messageObj = new MessageObj<string>();
// Start is called before the first frame update
void Start()
{
//定义一个订阅器,接收到信息时打印出信息
var subscription = messageObj.Subscribe(new Action<string>(msg =>
{
Debug.Log(msg);
}));
}
// Update is called once per frame
void Update()
{
//用于测试,按下空格键发送信息
if (Input.GetKeyDown(KeyCode.Space))
{
messageObj.Publish("Hello");
}
}
}
此时按下空格键,消息接收成功。
至此订阅器的初步功能验证完毕。 接下来的部分就是把订阅器加入到频道中,然后由频道发送信息,而订阅了该频道的消息主体则能够接收到信息。 这一部分过段时间再来完成。 文中有错误或表述不清的希望大家多多指出,谢谢。
|