如果在代码中出现耦合,想要解除耦合的情况,就可以使用事件中心的代码。

事件中心的工作原理

我们假设你的小区里面有一个快递驿站,

订阅事件(存快递):

  • 小明说:“驿站老板,如果有我的包裹(某个事件发生),请打电话通知我”

  • 小红说:“驿站老板,如果有我的快递(另外一个事件),请发短信通知我”

触发事件(快递到了):

  • 当驿站收到一个包裹(比如“玩家死亡”事件),老板会立刻通知所有订阅了这个包裹的人(小明、小红等)。

好处

  • 小明和小红不用天天盯着快递员(不用直接互相调用代码)

  • 驿站集中管理所有包裹(统一管理事件)

  • 随时可以取消订阅(“老板,以后不用通知我了!”)

一个简单的事件中心代码

通过上面的例子来模拟事件中心。

我们先制作一个快递驿站(创建事件中心)

快递驿站需要做什么工作?存放事件、通知玩家?

怎么存放事件?通过委托可以存放事件。

订阅事件?玩家注册事件(告诉事件中心,如果有我的快递,请通知我)

触发事件?事件中心检测到事件触发,会通知订阅该事件的玩家

public class EventCenter : MonoBehaviour {

    // 为什么是单例的?因为玩家要订阅事件
    public static EventCenter Instance;
    public Action<string> OnEvent; // 委托:可以存储多个方法

    void Awake() => Instance = this;

    // 触发事件:通知订阅的人
    public void TriggerEvent(string eventName) => OnEvent?.Invoke(eventName);
}

// 订阅者
public void Start(){
    // 告诉:事件中心,我的快递到了记得通知我哦
    EventCenter.Instance.OnEvent += (info)=>{
      // 快递到了,执行下面的逻辑
    };
}

// 触发者
void Update() {
    // 模拟快递到了情况
    if (Input.GetKeyDown(KeyCode.Space)) {
        // 通知事件执行,这个快递到了
        EventCenter.Instance.TriggerEvent("小明的快递");
    }
}

应用场景

1. 泛型版本的事件中心

想象事件中心是一个智能快递柜,泛型事件就是可以存放各种尺寸包裹的储物格。

// 我们设计一个智能快递柜
// 第一个参数是:每个快递柜门的编号
// 第二个参数是:包裹容器(就是这个柜门可以放任意大小的包裹)任意委托。然而,在从字典中检索这些委托并调用之前,你需要将它们转换回原始的委托类型
private Dictionary<string, Delegate> genericEventDict = new Dictionary<string, Delegate>();

接下来我们可以寄存包裹

public void AddListener<T>(string eventName, Action<T> action)
{
    // 判断快递门编号为eventName是否已经寄存过了这个包裹
    if (genericEventDict.ContainsKey(eventName))
    {
        // 如果有,那么在旧包裹中继续塞一个新包裹
        genericEventDict[eventName] = Delegate.Combine(genericEventDict[eventName], action);
    }
    else
    {
        // 如果没有,直接塞进去
        genericEventDict.Add(eventName, action);
    }
}

领取包裹

public void TriggerEvent<T>(string eventName, T arg)
{
    // 查找包裹,并验证是否是Action<T>类型的
    if (genericEventDict.TryGetValue(eventName, out Delegate del))
    {
        // 取出包裹,交给收件人
        (del as Action<T>)?.Invoke(arg);
    }
}