Unity 事件详解
在使用Unity的过程中, 事件(Unity Event)并不复杂,但是容易与很多相近的概念混淆。比如Event System(事件系统),Delegate(委托),C#的Event。
而UnityEvent其实只是C#委托的一个简单包装。
1. Unity Event 实例
首先把编程场景固定在一个跳跃动作时需要播放音效的过程。
if(jumping){ AudioSource audio = GetComponent<AudioSource>; audio.clip = Resources.Load<AudioClip>("jump"); audio.Play(); }
我们上面的代码是直接把播放声音的代码加进去,但这样的编程方式随着代码的增多后可读性会变差。而且可拓展性也会变差,当需要在玩家跳跃事同步发生其他事件时需要修改这部分代码,破坏了设计模式中的开闭原则(对修改关闭,对拓展开放)。所以上述代码需要进行修改。
// 添加UnityEvent关键字 ... public UnityEvent JumpEvent; ... // 修改Jump代码 if(jump){ JumpEvent.Invoke(); }
using UnityEngine public class PlayerAudio : MonoBehaviour{ AudioSource audioSource; void Start(){ audioSource = GetComponent<AudioSource>; PlatformerCharacter2D cc = GetComponent<PlatformerCharacter2D>();
// 找到之前的事件,为事件添加监听对象
// 对拓展开放,需要新的监听对象只需要在外面添加就行。 cc.JumpEvent.AddListener(OnPlayerJump); } }
我们跳跃写成了一个Unity事件,让需要响应的动作写成其他的函数,拓展时只需要为该事件添加监听对象。
2. Unity Event 的多参数形式
Unity Event不仅可以通过AddListener来订阅函数,还可以通过RemoveListener来删除订阅函数。订阅支持多次添加同一个函数,多添加几次就会多调用几次。如果订阅的函数需要参数,Unity Event也是支持的。
如下代码,MyEvent1是一种任意事件类型,参数为一个Vector3。
public class MyEvent1 : UnityEvent<Vector3>{}
3.Unity 事件与委托
UnityAction就是C#语法中的委托。初学者往往有疑问,那就是要实现事件的订阅和调用,使用委托的语法就足够了,根本不需要用到Unity事件。
using UnityEngine delegate void MyFunc1(); delegate int MyFunc2(int a); public class TestDelegate : MonoBehaviour{ void Start(){ MyFunc1 f1; f1 = F; MyFunc2 f2; f2 = G; f1(); f2(3); } void F(){ Debug.log("F"); } int G(int i ){ Debug.Log("G"+i); } }
以上就是委托的一段使用语法。delegate使得函数可以作为普通对象使用。有助于实现函数式编程。
每个委托的定义,必须清楚定义这类函数的参数数量,参数类型以及返回值类型。有了函数类型,就可以定义该变量的函数。除此之外,委托变量也可以通过参数传递或者添加到容器,即普通变量支持的操作,委托变量也能支持。委托的基本用法如下:
void Start(){ // 将函数用作参数传递。 CallFunc(F,G,9); // 列表,每个元素都是函数的作用 List<MyFun1> funcs = new List<MyFun1>(); // 添加 funcs.Add(f); foreach (MyFunc1 f in funcs){ f(); } } void CallFunc(Myfunc1 f, MyFunc2 g,int n){ ... }
拓展用法
1.委托作为事件使用
以下代码中,普通的函数变量g完全可以代替UnityEvent。Addlistener相当于+=。
int GA(int i){ Debug.log("GA"+i); return 0; } int GB(int i){ Debug.log("GB"+i); return 0; } void Start(){ Myfunc2 g = null; g+=GA; g+=GB; g(2); g+=GA; g-=GC; g(5); }
2.委托与事件的关系
namespace UnityEngine.Events { public delegate void UnityAction(); public delegate void UnityAction<T0>(T0 arg0); public delegate void UnityAction<T0,T1>(T0 arg0,T1 arg1); public delegate void UnityAction<T0,T1,T2>(T0 arg0,T1 arg1,T2 arg2); public delegate void UnityAction<T0,T1,T2,T3>(T0 arg0,T1 arg1,T2 arg2,T3 arg3); }
UnityAction是一种事先定义好的委托,也就是一种函数类型。UnityAction支持多个泛型,也可以方便的搭配UnityEvent使用。
从原理上说,委托可以代替Unity事件。但是在开发时最好使用UnityEvent,因为这符合开发规范并且会更加的便利。