本章节讲述消息通知模型在TPS项目中的使用,这种模型常见于项目中,表现多为Observer模式;Key-Value触发器模式; 文章末我会列出自己写的基于事件触发的模型,支持多种类型消息传递。

INTENSE的消息系统设计

举个例子,在 ThrowingEvents.cs 脚本中,有预先暴露的Event接口,观察事件触发时便会通知其他注册的事件。

  • 不使用单例,当前消息系统并不是,也就意味着每次需要初始化一个新的消息系统
  • 保持隔离性,INTENSE的消息系统设计就是每个不同类型的消息系统都会相互隔离。使用不同类型进行区分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//代码片段
public class ThrowingEvents
{
//···

public delegate void SingleThrowParamHandler(Exploder exp);

//···

public event SingleThrowParamHandler onThrowableExit;

//···

public void InvokeThrowableExit(Exploder exp)
{
if (onThrowableExit != null)
onThrowableExit(exp);
}
}

当前消息在 Canvas3D.cs 脚本中被注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Canvas3D : MonoBehaviour
{
//···
private void Start()
{
//···
player.SmbThrow.Events.onThrowableExit += OnThrowableExit;
//···
}
//···
private void OnThrowableExit(Exploder exp)
{
SetThrowableImage(player.SmbThrow.GetThrowableSprite());
SetThrowableName(player.SmbThrow.GetThrowableName());
ThrowableCountTexter(player.SmbThrow.GetThrowableCount());
}
}

当前消息(事件)在 ThrowCSMB.cs 脚本中被触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class ThrowCSMB : CustomPlayerSystemSMB
{
//···

private void ThrowCExploderClone()
{
if (cExploderClone)
{
//···
Events.InvokeOnThrow(cExploderClone);
//···
}
}
}

当这三个脚本运作起来的效果就是 玩家投掷->UI改变,此处有一个东西需要注意一下 就是 注册的先后顺序。

Event调用的先后顺序 先入先出

If I set up multiple event handlers, like so:

1
2
3
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;

The answer will be specific to the RetrieveDataCompleted event.
If it has the default backing store of a multi-cast delegate, then yes “they run in the same thread and sequentially in the order that are registered”.
Event在同一个线程内先注册的事件先被调用 - Stack Overflow FAQ

CASE

我个人写的消息通知模型源码,附带单元测试。

核心部分举例 ,框架核心就是RegisterEvent,UnregisterEvent两个方法的包装。所有的消息被维护到具体的几个dictionary中。