1.前言
1.1 普通的事件框架的缺陷
普通的事件框架,通常是创建一个EventHandler脚本文件,在里面定义大量的事件和对应的激活方法,然后再在对应的脚本当中编写监听方法,例如:
//EventHandler.cs
public static event Action OnSceneLoad;
public static void CallOnSceneLoad()
{OnSceneLoad?.Invoke();
}
然后,对应的脚本对事件进行订阅
//比如,这是某个脚本要监听这个事件
private void OnEnable()
{//订阅事件EventHandler.OnSceneLoad+=OnSceneLoad;
}
private void OnDisable()
{//取消订阅事件EventHandler.OnSceneLoad-=OnSceneLoad;
}
private void OnSceneLoad()
{//要执行的内容
}
这样无论是在代码中还是在Inspector窗口中,都十分的不直观,而且这样框架下,事件系统在后续的维护会很麻烦,因此,需要考虑到开发一套泛型开发框架,能够在Inspector窗口中快捷的修改事件订阅。
1.2 泛型事件框架的设计思路
对于泛型事件的框架,需要利用面向对象的思想来实现。
事件框架分为“事件广播者”和“事件监听者”。
-
事件广播者:负责广播事件,存储着事件广播的信息,需要一个泛型委托和一个广播函数组成,泛型委托用于存储订阅者,广播函数用于激活泛型委托。
-
事件监听者:负责监听来自事件广播者的事件,需要确定监听的广播者,并定义Unity事件用于在接收到广播之后执行相应的方法。
2.泛型事件框架的实现
下面是基本的泛型事件实现案例,可以根据个人的项目需求进行调整
2.1 创建事件广播者基类(SO)
这里可以将事件广播者继承ScriptableObject,但是不要写[CreateAssetMenu]标注,这个留给子类来写
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 事件SO基类
/// </summary>
public class BaseEventSO<T> : ScriptableObject
{[Header("事件描述")][TextArea]public string description;
public UnityAction<T> OnEventRaised;
public string lastSender; //最后一个发送广播的对象(可选)/// <summary>/// 广播事件/// </summary>/// <param name="value">要广播的值</param>/// <param name="sender">广播发送者(可选)</param>public void RaiseEvent(T value,object sender){OnEventRaised?.Invoke(value);lastSender = sender.ToString();}
}
2.2 创建事件监听者基类
事件监听者默认继承MonoBehavior即可,需要写上要监听的事件广播者,同时定义一个UnityEvent类型用于绑定接收到事件之后需要执行的函数
using System;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 事件监听器基类
/// </summary>
public class BaseEventListener<T> : MonoBehaviour
{/// <summary>/// 要监听的事件广播者/// </summary>public BaseEventSO<T> eventSO;/// <summary>/// 当eventSO中的事件被激活的之后,/// 执行response中订阅的方法/// </summary>public UnityEvent<T> response; private void OnEnable(){if (eventSO != null){//订阅事件eventSO.OnEventRaised += OnEventRaised;}}
private void OnDisable(){if (eventSO != null){//注销事件eventSO.OnEventRaised -= OnEventRaised;}}/// <summary>/// 接收到事件广播/// </summary>/// <param name="value"></param>private void OnEventRaised(T value){response.Invoke(value); //执行接收到事件后要执行的函数}
}
2.3 创建事件广播者的子类(SO)
这里这个子类就用来广播来自object类型的数据,继承其事件广播者的基类
using UnityEngine;
[CreateAssetMenu(fileName = "ObjectEventSO",menuName = "Events/ObjectEventSO")]
public class ObjectEventSO : BaseEventSO<object>
{}
2.4 创建事件监听者的子类
继承事件监听者的基类
using UnityEngine;
public class ObjectEventListener : BaseEventListener<object>
{}
2.5 创建事件广播者的SO文件
有了上述的框架基础之后,我们就可以实现这样的可视化操作:
-
在project窗口中创建多个事件的SO文件,以用于定义多个不同的事件
-
在指定对象的Inpector窗口中添加多个事件监听者,通过可视化的方式管理要监听的事件(引用SO文件)以及事件激活后要执行的方法。
2.6 订阅事件
将事件监听者子类的脚本挂载到场景中要监听事件的对象上,同时,根据自身项目需求,给事件监听者挂载要监听的事件广播者,并为该事件订阅单个或多个方法。
由于事件监听者已经独立化,所以你可以为一个对象添加多个事件监听者脚本,以方便监听多个事件。
2.7 广播事件
在需要发送广播的对象的脚本中,定义变量来引用指定的事件广播者(引用事件广播者的SO文件),然后在合适的时机广播事件即可。