http://zijan.iteye.com/blog/871207php
翻译自:
http://www.everyday3d.com/blog/index.php/2010/10/04/c-events-and-unity3d/
zijan译
(括号内是译者本身对文章和技术的理解)
(Unity3D是如今愈来愈流行的3D游戏引擎,它支持JavaScript,c#和Boo语言。若是你是个Unity3D的爱好者,但只会JavaScript。这里有一篇文章关于处理事件和消息传递,也许更适合你。A Useful Messaging System)
你知道C#有一个内置的事件机制吗?这个东东在Unity3D里也很是好用。下面举一个例子。
为了响应一个GameObject的事件分发,你一般要创建一个脚本继承MonoBehaviour而且实现你须要的方法。好比你想对鼠标悬停做出反应,就要建立OnMouseOver方法。一般代码会像这个样子:
c#
void OnMouseOver () { renderer.material.color = Color.red; }
这样工做没问题。但若是你想通知另一个对象响应这个事件(OnMouseOver事件)怎么办?
第一种方式是保持另外对象的脚本引用,而后在你的OnMouseOver方法中调用它:
设计模式
public MyScript myScript; void OnMouseOver () { myScript.NotifyMouseOver(); }
这样作没问题,可是不够好。由于你须要一直保持另一个对象的引用,若是想通知多个对象要保持多个引用。代码会变得很乱。
Messages 消息
另外一个办法是用SendMessage或SendMessageUpwards方法。看上去这是解决问题的最好办法,可是这些方法存在严重的缺陷,以个人观点,你应该尽可能不去使用它们。
这些方法的语法并不灵活,你须要传递一个方法名字的字符串,这样作很容易出错。另外这些方法只能用在同一个对象的附属关系中。换句话说你只能在下面几种状况中调用SendMessage或SendMessageUpwards方法,这些方法的脚本被关联到同一个GameObject中,或者被关联到这个GameObject的祖先关系对象中。
Events 事件
幸运的是有一个更好的解决办法,这就是C#内置的事件机制。我不在这里过多的描述机制是如何工做的,你若是有兴趣能够学习相关的知识,访问MSDN手册。(译者推荐另一篇文章,C# 中的委托和事件)
如今让咱们看看如何在Unity3D中使用事件机制。
学习
using UnityEngine; public class EventDispatcher : MonoBehaviour { public delegate void EventHandler(GameObject e); public event EventHandler MouseOver; void OnMouseOver () { if (MouseOver != null) MouseOver (this.gameObject); } }
若是你不知道这段代码到底干什么,先不要着急。重要的是一旦你把这段代码关联到一个GameObject,只要在整个项目的任何一个脚本中保持这个对象,你就能够像下面这样处理事件:
ui
private GameObject s; [...] s.GetComponent<EventDispatcher>().MouseOver += Listener; [...] void Listener(GameObject g) { // g is being hovered, do something... }
这种方式比用消息更灵活,由于它能够被用在任何一个脚本中,而不只仅在同一个对象附属关系中。若是在整个应用中保持一个单例模式的对象,你就能够监放任何从这个对象分发出来的事件。
另一个重要特色,同一个监听方法能够响应不一样对象的事件。经过传递事件源对象的引用做为参数,你总会知道哪一个对象分发了事件,就像个人代码展现的那样。(对于这句话能够这样理解,假如游戏中扔一颗导弹炸死了一个小兵并致使坦克减血,小兵死亡和坦克减血这两个事件都触发了同一个监听方法-玩家得分,经过传递进来的事件源对象,就能知道小兵仍是坦克触发了玩家得分这个监听方法。)
References, controllers and MVC
如今让咱们比较一下第一和第三种方式。在最开始的例子中(第一种方式保持另外对象的脚本引用),你须要在事件分发代码中保持监听者的对象引用,我说了这不是一个好主意。在用内置事件机制,改进的版本中(第三种方式),你须要在监听者代码中保持事件分发者的引用。你也许会问,为何后者更好?
首先,分发者不须要知道本身事件的监听者是谁,不须要知道有多少监听者。它只负责事件的发送。在最开始的例子中(第一种方式),若是要告诉分发者中止通知监听者,你能想象这种程序判断有多么笨重吗?
事件机制中,是由监听者本身决定监听什么事件,何时开始监听,何时中止监听。像这样的对象一般用于管理程序的状态或者执行某些游戏逻辑。这个就叫作控制器,借用MVC设计模式的概念。这样咱们的代码会更清晰,不易出错。(译者认为观察者设计模式更符合)
最后一点,我喜欢重载“+=”操做符去添加监听方法。如今你也许可以猜到,若是想结束监听某个事件,能够这么写:
this
s.GetComponent<EventDispatcher>().MouseOver -= Listener;
固然你能够建立一个通用的EventDispatcher类,实现全部GameObject可以分发的事件。能够参看下面的代码。另外在实现OnGUI事件时要特别当心,若是想知道为何,读读这篇文章。
spa