Unity中的事件系统演进

Unity本身是没有所谓的事件系统的,这里所说的事件系统指是使用C#语言的Delegate机制实现的一种类似观察者模式的系统,可以将事件的产生与事件的处理相分离,使得系统可以尽量的解耦合。在Unity Community中事件系统()也称消息系统()。

以一个简单的例子来引入事件系统。

当玩家接到从屏幕上方掉落下来的道具时,玩家的生命值加1。这是一个很基础的功能需求,这类需求充斥着游戏的所有地方。当然我们可以不使用事件系统,直接在OnTriggerEnter方法中给该玩家的生命值加1就好了,但是,这将使得检测碰撞的这块代码直接引用了玩家属性管理的代码,也就是代码的紧耦合。而且,在后来的某一天,我们又想让接到道具的同时还在界面上显示一个图标,这时我们又需要在这里引用界面相关的代码。后来,我们又希望能播放一段音效……

使用事件系统,我们只需要在OnTriggerEnter函数中添加一行代码:

在玩家属性管理代码中,我们会给玩家生命值加1,在UI管理代码中我们会显示一个新的图标,在声音管理代码中我们播放了一段音效……这就是基于事件系统的好处。

 

CSharpEventManager

UnifyCommunity中能找到的最早的C#事件系统是CSharpEventManager,http://www.unifycommunity.com/wiki/index.php?title=CSharpEventManager,从其编辑历史可以看到创建于2009年9月,作者为Billy “brian” Fletcher。

CSharpEventManager定义了两个基类IEvent和IEventListener,分别用于表示事件类和事件监听类,事件类定义了名称和携带的参数,因为使用了object类型的参数,所以可以由定义者任意指定数据内容,IEventListener定义了一个统一的回调接口,所有需要注册事件的对象都必须从这个基类派生。EventManager自身则需要绑定到一个GameObject上,由GameObject的Update方法来驱动事件的回调过程,当然也提供了立即触发事件的接口。

整个代码比较简洁,但两个基类的定义让使用者有一些束缚。如果事件仅仅只需要传递一个float参数,或者根本就不需要参数,也要为此事件定义一个Event实现,事件的回调因为只有一个固定的接口,如果某个对象需要监听多个事件,则需要HandleEvent里根据事件名进行区分,这都使得这个事件管理器的使用变得不怎么灵活。

CSharpMessenger

基于对这两个问题的改进,Rod Hyde于实现了CSharpMessenger (http://www.unifycommunity.com/wiki/index.php?title=CSharpMessenger),时间也是2009年9月,比CSharpEventManager稍晚几天。

CSharpMessenger使用C#的泛型来代替CSharpEventManager的参数传递方式,这就避免了IEvent和IEventListener接口的使用,另外把Messenger类定义为了static,不再需要将其绑定到GameObject之上,这也使得其事件派发方式只能是立即触发回调。

使用泛型之后,事件的定义、回调接口的定义和事件的派发都变的很直接。

在玩家属性管理代码中这样注册事件:

而在OnTriggerEnter中把事件派发的代码修改为:

比起CSharpEventManager确实要简洁了很多。

CSharpMessenger Extended

CSharpMessenger让事件系统的使用变得更简洁了,但使用者却发现了一些问题:当玩家属性管理对ItemCollected事件的响应代码完成之后,声音管理的代码也准备添加。因为不论接到何种类型的道具都播放同一种音效,所以声音管理代码的添加者定义了这样一个回调函数,并将其注册到事件系统中:

当事件被触发的时候,错误出现了,因为OnItemCollected()方法不符合Invoke时的delegate要求,程序抛出了异常。

为了解决这个问题,Magnus Wolffelt对CSharpMessenger做了一点扩展,名字就叫做CSharpMessenger Extended,时间是2010年1月 (http://www.unifycommunity.com/wiki/index.php?title=CSharpMessenger_Extended)。

Magnus Wolffelt的做法很简单,在每次AddListener的时候对callback的GetType()返回值进行检查,是否与这个事件已有的callback GetType()返回值相同,如果不同则不允许添加。类似的,还有其他一些有可能产生运行时错误的检查,比如事件必须有至少一个Listener,当然这也是可选的检查项。

Advanced CSharp Messenger

使用过事件系统的人都会有回调到空指针的记忆,当然C#里不会有空指针的错误,但是会有MissingReferenceException的异常。而且Unity里面因为LoadLevel会让所有的资源和脚本重新加载,出现异常的概率会更大,这要求写代码的人很清楚的知道什么时候该RemoveListener。

这了解决这个问题,Ilya Suzdalnitski对CSharpMessenger又做了一次扩展,这次在CSharpMessenger Extended的基础上进行,时间是2011年11月,修改后叫Advanced CSharp Messenger。(http://www.unifycommunity.com/wiki/index.php?title=Advanced_CSharp_Messenger)

对这个问题的修改方法很简单,创建一个Gameobject,绑定事件管理器到这个对象上,这个对象将能够收到OnLevelWasLoaded的回调,在这里把之前的所有事件都清除掉即可。

但除此之外Ilya还做了一些修改,比如在清除的时候可以保留一些不希望清除的事件回调,只需要在注册完成后调用MarkAsPermanent(string eventType)声明一下即可,然后加了一个日志开关,另外还将原来的Messenger泛型对象改为了Messenger内部的泛型方法,简单来说就是原来的Messenger<T>.AddListener(…)改成了Messenger.AddListener<T>(…),这个并不是为了解决什么问题,只是编程人员的个人喜好。

从CSharpMessenger Extended到Advanced CSharp Messenger并不是有什么重大问题,只要编程人员清楚应该在什么时候RemoveListener并不会有什么问题,但接口方法的改变则涉及到编程人员的习惯问题,每个人都有改造代码适应自己习惯的癖好,这就看个人选择了。

原创文章,转载请注明: 转载自All-iPad.net

本文链接地址: Unity中的事件系统演进

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity框架事件系统是一种消息传递机制,用于在各个组件之间发送和接收消息。它基于发布/订阅模式,其一个组件发布事件,而其他组件通过订阅该事件来接收消息并做出相应的响应。 在Unity事件系统包括两种类型的事件Unity事件和C#事件Unity事件是在Unity编辑器定义的事件,可以在脚本访问。C#事件是由C#编写的自定义事件。 使用Unity事件系统的步骤包括: 1. 定义事件:创建一个事件类来定义事件的名称和参数。 2. 注册事件:在需要接收该事件的组件注册该事件。 3. 发布事件:当某个条件满足时,发布该事件。 4. 响应事件:在接收到事件时,执行相应的操作。 以下是一个示例代码,展示如何在Unity使用事件系统: ``` //定义事件类 public class MyEvent : UnityEvent<int, string> {} //注册事件 public MyEvent myEvent = new MyEvent(); void Start() { myEvent.AddListener(MyEventHandler); } //发布事件 if (condition) { myEvent.Invoke(1, "message"); } //响应事件 void MyEventHandler(int value, string message) { Debug.Log(message + " " + value); } ``` 在这个示例,我们定义了一个名为MyEvent的事件类,该事件有两个参数:int和string类型。然后,我们在Start方法注册了该事件,并在某个条件得到满足时发布了该事件。最后,我们在MyEventHandler方法响应了该事件,输出了传递的参数。 总的来说,Unity框架事件系统是一种非常有用的消息传递机制,可以帮助开发者优化代码结构,提高代码的可读性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值