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