07. 泛型事件框架
事件监听和发布
我们要实现这样一个功能,当玩家在 Map 场景中点击一个图标的时候,需要发送事件给场景管理器,场景管理器监听事件,然后执行切换场景的操作
在勇士传说中,我们使用 ScriptableObject.UnityAction 发布事件,以及监听事件,那时候每种类型都有一个 ScriptableObject,非常繁琐。所以我们这次使用了泛型来简化代码编写
编写代码
目录结构
在 Scripts/Events 目录下,创建 Editor(用于扩展 Inspector 窗口)、MonoBehaviour(用于事件监听)、ScriptableObject(用于事件发布)
在 Game Data 目录下,创建 Events(用于创建事件)
ScriptableObject
首先,我们需要定义一个 BaseEventSO,代码如下
在 BaseEventSO 里面,定义了一个 UnityAction,用于事件发布和订阅。当发布者调用 RaiseEvent 的时候,就会通知订阅者,同时记下当前的发布者
然后需要定义一个 ObjectEventSO,用于传递 object 类型的事件
MonoBehaviour
和 ScriptableObject 一样,我们需要定义 BaseEventListener,用于监听事件。它的写法和勇士传说里很多监听者的写法一样,在OnEnable
的时候注册事件监听,在OnDisable
的时候注销事件监听,在事件产生的时候调用 UnityEvent 的 Invoke
同样我们也写一个监听 object 事件的类
Events
因为我们已经创建好 ObjectEventSO 了,所以我们可以创建一个 Event 出来,名字叫做LoadRoomEvent
在 RoomPrefab 里发布 LoadRoomEvent
在 SceneLoadManager 里面监听 LoadRoomEvent
当 RoomPrefab 发布事件的时候,SceneLoadManager 就能监听到这个事件,然后调用OnLoadRoomEvent
方法
扩展 Inspector
如上图所示,当我们查看 LoadRoomEvent 的时候,可以看到消息的发布者,以及消息的订阅者。这是怎么实现的呢?
消息的发布者,其实在 ScriptableObject/BaseEventSO 里面已经实现了
消息的订阅者,代码比较复杂,我贴出来,大家看看就好
BaseEventSOEditor
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(BaseEventSO<>))]
public class BaseEventSOEditor<T> : Editor
{
private BaseEventSO<T> baseEventSO;
private void OnEnable() {
if (baseEventSO == null)
{
baseEventSO = target as BaseEventSO<T>;
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.LabelField($"订阅数量: {GetListeners().Count}");
foreach (var listener in GetListeners())
{
EditorGUILayout.LabelField(listener.ToString());
}
}
private List<MonoBehaviour> GetListeners()
{
List<MonoBehaviour> listeners = new List<MonoBehaviour>();
if (baseEventSO == null || baseEventSO.OnEventRaised == null)
{
return listeners;
}
var subscribers = baseEventSO.OnEventRaised.GetInvocationList();
foreach (var subscriber in subscribers)
{
var obj = subscriber.Target as MonoBehaviour;
if (!listeners.Contains(obj))
{
listeners.Add(obj);
}
}
return listeners;
}
}
ObjectEventSOEditor