Unity中的事件系统历史

发布时间:2016-12-11 22:03:45 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"Unity中的事件系统历史",主要涉及到Unity中的事件系统历史方面的内容,对于Unity中的事件系统历史感兴趣的同学可以参考一下。

  分类: Unity3D 2012-11-29 11:25 568人阅读 评论(0) 收藏 举报 目录(?)[+] CSharpEventManagerCSharpMessengerCSharpMessenger ExtendedAdvanced CSharp Messenger Unity本身是没有所谓的事件系统的,这里所说的事件系统指是使用C#语言的Delegate机制实现的一种类似观察者模式的系统,可以将事件的产生与事件的处理相分离,使得系统可以尽量的解耦合。在Unity Community中事件系统(Event Manager)也称消息系统(Messenger)。 以一个简单的例子来引入事件系统。 当玩家接到从屏幕上方掉落下来的道具时,玩家的生命值加1。这是一个很基础的功能需求,这类需求充斥着游戏的所有地方。当然我们可以不使用事件系统,直接在OnTriggerEnter方法中给该玩家的生命值加1就好了,但是,这将使得检测碰撞的这块代码直接引用了玩家属性管理的代码,也就是代码的紧耦合。而且,在后来的某一天,我们又想让接到道具的同时还在界面上显示一个图标,这时我们又需要在这里引用界面相关的代码。后来,我们又希望能播放一段音效…… 使用事件系统,我们只需要在OnTriggerEnter函数中添加一行代码: C# EventManager.TriggerEvent("ItemCollected", itemType); 1 EventManager.TriggerEvent("ItemCollected", itemType); 在玩家属性管理代码中,我们会给玩家生命值加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之上,这也使得其事件派发方式只能是立即触发回调。 使用泛型之后,事件的定义、回调接口的定义和事件的派发都变的很直接。 C# void OnItemCollected(int itemType) { // 生命值加1 } 1 2 3 4 void OnItemCollected(int itemType) { // 生命值加1 } 在玩家属性管理代码中这样注册事件: C# Messenger&lt;int&gt;.AddListener("ItemCollected", OnItemCollected); 1 Messenger&lt;int&gt;.AddListener("ItemCollected", OnItemCollected); 而在OnTriggerEnter中把事件派发的代码修改为: C# Messenger&lt;int&gt;.Invoke("ItemCollected", itemType); 1 Messenger&lt;int&gt;.Invoke("ItemCollected", itemType); 比起CSharpEventManager确实要简洁了很多。 CSharpMessenger Extended CSharpMessenger让事件系统的使用变得更简洁了,但使用者却发现了一些问题:当玩家属性管理对ItemCollected事件的响应代码完成之后,声音管理的代码也准备添加。因为不论接到何种类型的道具都播放同一种音效,所以声音管理代码的添加者定义了这样一个回调函数,并将其注册到事件系统中: C# void OnItemCollected() { // 播放音效 } 1 2 3 4 void OnItemCollected() { // 播放音效 } 当事件被触发的时候,错误出现了,因为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中的事件系统演进

上一篇:Bash 实现MR
下一篇:随机网络的扩张

相关文章

相关评论