简介:
定位:为可视化而生;
方法注册:所有类型(无参或者泛型)的UnityEvent都支持静态注册无参和单参方法(参数类型授限)。静态注册即在inspector面板上配置;
参数传递:静态传递只支持单参数方法,多参数必须在Invoke中动态传递。
1.Inspector配置/纯代码绑定
对于UnityEvent的使用,根据是否需要Inspector可视化配置分为两种声明方式。
1.1 Inspector配置
在 Unity 中声明带参数的UnityEvent主要通过继承泛型类来实现。
由于UnityEvent的泛型版本(如UnityEvent<T>)是抽象类或需要在 Inspector 中序列化,因此不能直接在字段中声明为new UnityEvent<int>()并期望其在编辑器中完美工作。
步骤一:定义自定义事件类
你需要创建一个继承自UnityEvent<T0, T1...>的新类,并加上[System.Serializable]特性,以便在 Unity 编辑器的 Inspector 面板中显示和配置。必须添加[System.Serializable]特性,否则该事件可能无法在 Inspector 中正确显示持久化监听器(Persistent Listeners)。
注:[System.Serializable]加在类或结构体定义上,标记“这个类能被序列化”,[SerializeField]管“字段”,标记“这个私有字段要参与序列化并显示在 Inspector”。
using UnityEngine; using UnityEngine.Events; //支持一个参数 [System.Serializable] public class IntEvent : UnityEvent<int> { } //支持四个参数 [System.Serializable] public class MyCustomEvent : UnityEvent<string, int, bool, float> { }步骤二:在 MonoBehaviour 中使用
在你的脚本中声明上述自定义类的公共字段。
public class EventExample : MonoBehaviour { // 在 Inspector 中可以看到这个字段,并可以添加监听者 //此处声明后并未初始化,应该是Unity在inspector生成中进行了初始化 public IntEvent onScoreChanged; void Start() { // 代码动态添加监听 onScoreChanged.AddListener(HandleScoreChange); } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { // 触发事件,传入参数 onScoreChanged.Invoke(100); } } // 监听方法,签名必须与泛型参数匹配 void HandleScoreChange(int score) { Debug.Log("Current Score: " + score); } }将脚本添加至游戏对象,则在inspector面板中,可看到如下设置界面
1.2 纯代码方式(不需要Inspector配置)
如果你不需要在 Unity 编辑器的 Inspector 面板中绑定事件(即所有监听器都通过代码动态添加),那么你可以不创建子类,直接使用泛型实例。
using UnityEngine; using UnityEngine.Events; public class CodeOnlyEvent : MonoBehaviour { // 直接声明泛型字段,无需创建子类 private UnityEvent<int> myEvent = new UnityEvent<int>(); void Start() { // 必须通过代码添加监听 myEvent.AddListener(HandleNumber); // 触发 myEvent.Invoke(42); } void HandleNumber(int num) { Debug.Log(num); } }缺点:
- 在 Inspector 中不可见。
- 无法保存持久化监听器(Persistent Listeners)。
- 通常用于纯代码架构或临时事件。
1.3 测试疑问?
针对inspector配置须通过继承泛型类来实现这个说法,在以下测试代码中发现直接声明UnityEvent字段在inspector中也是可编辑和配置的呀??有点懵了。
此外,并不需要去做初始化,关于这个的解释如下:对于public或[SerializeField]标记的这些受支持类型,Unity 会在序列化阶段自动实例化它们,所以即使你不写= new UnityEvent<int>(),它也不会为 null,且在 Inspector 中可见。
using UnityEngine.UIElements; using UnityEngine; using UnityEngine.Events; public class EventExample : MonoBehaviour { // 在 Inspector 中可以看到这个字段,并可以添加监听者 public IntEvent onScoreChanged; public UnityEvent onTest; public UnityEvent<int> onTest2; public UnityEvent<PlayerData> onTest3; public UnityEvent<int,float> onTest4; private void Awake() { } void Start() { if (onTest != null) { Debug.Log("OnTest initialize"); } if (onTest2 != null) { Debug.Log("OnTest2 initialize"); } if (onTest3 != null) { Debug.Log("OnTest3 initialize"); } if (onTest4 != null) { Debug.Log("OnTest4 initialize"); } // 代码动态添加监听 onScoreChanged.AddListener(HandleScoreChange); } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { // 触发事件,传入参数 onScoreChanged.Invoke(100); } } // 监听方法,签名必须与泛型参数匹配 void HandleScoreChange(int score) { Debug.Log("Current Score: " + score); } } [System.Serializable] public class IntEvent : UnityEvent<int> { } [System.Serializable] public struct PlayerData { public int id; public string name; }2. 使用 C# 原生Action/Delegate(替代方案)
如果你觉得 UnityEvent 的声明太繁琐,且不需要 Inspector 可视化,可以使用 C# 原生的Action或event。这在代码层面更简洁,但功能不同。
using System; using UnityEngine; public class ActionExample : MonoBehaviour { // 声明简单,支持任意数量参数 public event Action<int, string> OnDataReceived; void Trigger() { // 触发前检查 null OnDataReceived?.Invoke(100, "Hello"); } }