C# 中的委托与事件
委托是一种对方法的引用(类似指针),实例委托时,可以与任何具有相同签名的方法关联。
委托使用 delegate 关键字声明,在 delegate 前加上 event 后,则将委托声明为事件,实际上为委托加上更多的限制,事件只能在声明类中调用(发布),且只能使用 +=,-+ 运算符 订阅 / 取消 。
using UnityEngine;// 发布类public class DelegateScript : MonoBehaviour{// 定义委托public delegate void SampleDelegate(int i);// 声明委托public SampleDelegate sampleDelegate;// 将委托声明为事件public event SampleDelegate sampleDelegateEvent;private void Start(){// 发布委托sampleDelegate?.Invoke(1);// 发布事件,只能在本类中调用sampleDelegateEvent?.Invoke(2);}}
using UnityEngine;// 订阅类public class DelegateTest : MonoBehaviour{public DelegateScript delegateScript; // 发布类void Start(){// 实例化委托(关联方法)delegateScript.sampleDelegate = Register;// 这个动作则叫订阅事件,只能使用 +=delegateScript.sampleDelegateEvent += Register;// 委托可在其他类中调用delegateScript.sampleDelegate(3);// 而事件不能在发布类之外调用,下面这句将报错delegateScript.sampleDelegateEvent(4);}void Register(int i){Debug.Log($"delegate {i} invoked.");}}
在平常使用时,用不用 event 关键字区别不大,但在C#文档中定义事件专指使用 event 关键字的情况。
C# 内置委托 Action and Func
在 C# System 命名空间下,提供预定义的 Action, Func 可直接使用。
using System;using UnityEngine;// 发布类public class DelegateScript : MonoBehaviour{// 预定义的 Action,无返回值public event Action<int> action;// 预定义的 Func,有返回值public event Func<int, string> func;// 预定义的 EventHandlerpublic event EventHandler<MyEventArgs> eventHandler;private void Start(){action?.Invoke(1);func?.Invoke(2);eventHandler.Invoke(this, new MyEventArgs(3));}}// 自定义事件数据public class MyEventArgs : EventArgs{public int value;public MyEventArgs(int value){this.value = value;}}
using UnityEngine;// 订阅类public class DelegateTest : MonoBehaviour{public DelegateScript delegateScript; // 引用发布类void Start(){delegateScript.action += Register;delegateScript.func += FuncRegister;delegateScript.eventHandler += EventHandlerRegister;}void Register(int i){Debug.Log($"delegate {i} invoked.");}string FuncRegister(int i){string result = $"delegate {i} invoked.";Debug.Log(result);return result;}void EventHandlerRegister(object sender, MyEventArgs e){Debug.Log($"delegate {e.value} invoked.");}}
UnityAction
由 Unity 实现的预定义 delegate。和 Action, Func 类似。
UnityEvents
可通过 Editor 进行配置绑定事件,非常方便。
using UnityEngine.Events;...// 定义 UnityEventpublic UnityEvent onStateEnter;void Start(){onStateEnter?.Invoke();}
默认的 UnityEvent 只支持无参数方法,可自定义有参数的 UnityEvent
using UnityEngine.Events;[System.Serializable]public class UnityEventInt : UnityEvent<int> {}
调用 UnityEvent
...public UnityEventInt onStateEnter;void Start(){onStateEnter?.Invoke(10);}
除了通过编辑器,也可用脚本方式绑定事件。
onStateEnter.AddListener(() => Debug.Log("Execute")); // 针对无参的 UnityEvent
Unity Messaging System
这是目前新的 UI 系统使用的消息系统(如 IPointerEnterHandler IDropHandler 等),用于取代传统的 SendMessage 方式。下面演示如何自定义消息,让状态机传递消息给目标对象。
首先,通过实现 IEventSystemHandler 定义一个新的消息接口
using UnityEngine;using UnityEngine.EventSystems;// 定义消息接口,必须实现 IEventSystemHandler 接口public interface IStateEnter : IEventSystemHandler{// 目的是将状态机中的 AnimatorStateInfo 传递给目标对象void OnStateEnter(AnimatorStateInfo stateInfo);}
在状态机中发布消息
using UnityEngine.EventSystems;public class SMB: StateMachineBehaviour{public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){// 通过 ExecuteEvents.Execute 工具函数来发布消息// 第一个参数指定消息接收的对象// 参数 x 映射前面泛型消息接口(<IStateEnter>),其他参数不重要ExecuteEvents.Execute<IStateEnter>(animator.gameObject, null, (x, y) => x.OnStateEnter(stateInfo));}}
接收消息,只有在目标对象上实现了 IStateEnter 接口,才能接收消息
using UnityEngine;public class Player : MonoBehaviour, IStateEnter{public void OnStateEnter(AnimatorStateInfo stateInfo){Debug.Log(stateInfo.xxx);}}
使用 Messaging System 需要事先指定接收的对象,就如 UI EventSystem 通过 Raycast 找到对象后,再将 IDropHandler 的消息送到该对象。
————————————————
原文链接:/kenight/article/details/98876335