C# 事件和 Unity3D

Unity3D是現在越來越流行的3D遊戲引擎,它支援JavaScriptc#Boo語言。如果你是個Unity3D的愛好者,但只會JavaScript。這裏有一篇文章關於處理事件和消息傳遞,也許更適合你。A Useful Messaging System

你知道C#有一個內置的事件機制嗎?這個東東在Unity3D裏也非常好用。下面舉一個例子。 

為了回應一個GameObject的事件分發,你通常要建立一個腳本繼承MonoBehaviour並且實現你需要的方法。比如你想對滑鼠懸停作出反應,就要創建OnMouseOver方法。通常代碼會像這個樣子: < xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />

C#代碼

void OnMouseOver ()

{

renderer.material.color = Color.red;

}

 

 

這樣工作沒問題。但如果你想通知另外一個物件響應這個事件(OnMouseOver事件)怎麼辦?

第一種方式是保持另外物件的腳本引用,然後在你的OnMouseOver方法中調用它:

C#代碼

public MyScript myScript;

void OnMouseOver () {

myScript.NotifyMouseOver();

}

 

 

 

 




這樣做沒問題,但是不夠好。因為你需要一直保持另外一個物件的引用,如果想通知多個物件要保持多個引用。代碼會變得很亂。 

Messages 消息

另一個辦法是用SendMessageSendMessageUpwards方法。看上去這是解決問題的最好辦法,但是這些方法存在嚴重的缺陷,以我的觀點,你應該儘量不去使用它們。 

這些方法的語法並不靈活,你需要傳遞一個方法名字的字串,這樣做很容易出錯。另外這些方法只能用在同一個物件的附屬關係中。換句話說你只能在下面幾種情況中調用SendMessageSendMessageUpwards方法,這些方法的腳本被關聯到同一個GameObject中,或者被關聯到這個GameObject的祖先關係物件中。 

Events 事件

幸運的是有一個更好的解決辦法,這就是C#內置的事件機制。我不在這裏過多的描述機制是如何工作的,你如果有興趣可以學習相關的知識,訪問MSDN手冊。(C# 中的委託和事件

現在讓我們看看如何在Unity3D中使用事件機制。

C#代碼

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,只要在整個項目的任何一個腳本中保持這個物件,你就可以像下面這樣處理事件:

C#代碼

private GameObject s;

[...]

s.GetComponent<EventDispatcher>().MouseOver += Listener;

[...]

void Listener(GameObject g) {

// g is being hovered, do something...

}

 

 

 

 

 

 

 



這種方式比用消息更靈活,因為它可以被用在任何一個腳本中,而不僅僅在同一個物件附屬關係中。如果在整個應用中保持一個單例模式的物件,你就可以監聽任何從這個物件分發出來的事件。

另外一個重要特點,同一個監聽方法可以響應不同物件的事件。通過傳遞事件源對象的引用作為參數,你總會知道哪個對象分發了事件,就像我的代碼展示的那樣。(對於這句話可以這樣理解,假如遊戲中扔一顆導彈炸死了一個小兵並導致坦克減血,小兵死亡和坦克減血這兩個事件都觸發了同一個監聽方法-玩家得分,通過傳遞進來的事件源物件,就能知道小兵還是坦克觸發了玩家得分這個監聽方法。)

References, controllers and MVC

現在讓我們比較一下第一和第三種方式。在最開始的例子中(第一種方式保持另外物件的腳本引用),你需要在事件分發代碼中保持監聽者的物件引用,我說了這不是一個好主意。在用內置事件機制,改進的版本中(第三種方式),你需要在監聽者代碼中保持事件分發者的引用。你也許會問,為什麼後者更好?

首先,分發者不需要知道自己事件的監聽者是誰,不需要知道有多少監聽者。它只負責事件的發送。在最開始的例子中(第一種方式),如果要告訴分發者停止通知監聽者,你能想像這種程式判斷有多麼笨重嗎?

事件機制中,是由監聽者自己決定監聽什麼事件,什麼時候開始監聽,什麼時候停止監聽。像這樣的物件通常用於管理程式的狀態或者執行某些遊戲邏輯。這個就叫做控制器,借用MVC設計模式的概念。這樣我們的代碼會更清晰,不易出錯。(譯者認為觀察者設計模式更符合)

最後一點,我喜歡重載“+=”操作符去添加監聽方法。現在你也許能夠猜到,如果想結束監聽某個事件,可以這麼寫:

C#代碼

s.GetComponent<EventDispatcher>().MouseOver -= Listener;

 



當然你可以創建一個通用的EventDispatcher類,實現所有GameObject能夠分發的事件。可以參看下面的代碼。另外在實現OnGUI事件時要特別小心,如果想知道為什麼,讀讀這篇文章

C#代碼

using UnityEngine;

using System.Collections;

 

/**

* A simple event dispatcher - allows to listen to events in one GameObject from another GameObject

*

* Author: Bartek Drozdz (bartek [at] everyday3d [dot] com)

*

* Usage:

* Add this script to the object that is supposed to dispatch events.

* In another objects follow this pattern to register as listener at intercept events:

 

void Start () {

EventDispatcher ev = GameObject.Find("someObject").GetComponent<EventDispatcher>();

ev.MouseDown += ListeningFunction; // Register the listener (and experience the beauty of overloaded operators!)

}

 

void ListeningFunction (GameObject e) {

e.transform.Rotate(20, 0, 0); // 'e' is the game object that dispatched the event

e.GetComponent<EventDispatcher>().MouseDown -= ListeningFunction; // Remove the listener

}

 

* This class does not implement all standards events, nor does it allow dispatching custom events,

* but you shold have no problem adding all the other methods.

*/

public class EventDispatcher : MonoBehaviour

{

 

public delegate void EventHandler (GameObject e);

public delegate void CollisionHandler (GameObject e, Collision c);

 

public event EventHandler MouseOver;

void OnMouseOver ()

{

if (MouseOver != null)

MouseOver (this.gameObject);

}

 

public event EventHandler MouseDown;

void OnMouseDown ()

{

if (MouseDown != null)

MouseDown (this.gameObject);

}

 

public event EventHandler MouseEnter;

void OnMouseEnter ()

{

if (MouseEnter != null)

MouseEnter (this.gameObject);

}

 

 

public event EventHandler MouseExit;

void OnMouseExit ()

{

if (MouseExit != null)

MouseExit (this.gameObject);

}

 

public event EventHandler BecameVisible;

void OnBecameVisible ()

{

if (BecameVisible != null)

BecameVisible (this.gameObject);

}

 

public event EventHandler BecameInvisible;

void OnBecameInvisible ()

{

if (BecameInvisible != null)

BecameInvisible (this.gameObject);

}

 

public event CollisionHandler CollisionEnter;

void OnCollisionEnter (Collision c)

{

if (CollisionEnter != null)

CollisionEnter (this.gameObject, c);

}

 

public event CollisionHandler CollisionExit;

void OnCollisionExit (Collision c)

{

if (CollisionExit != null)

CollisionExit (this.gameObject, c);

}

 

}

posted @ 2013-02-22 18:29  EricTang  阅读(758)  评论(0编辑  收藏  举报