C# 事件和Unity3D

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#

C#代碼 複製代碼  收藏代碼
  1. void OnMouseOver () {  
  2.   renderer.material.color = Color.red;  
  3. }  
void OnMouseOver () {
  renderer.material.color = Color.red;
}




這樣工做沒問題。但若是你想通知另一個對象響應這個事件(OnMouseOver事件)怎麼辦?

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

設計模式

C#代碼 複製代碼  收藏代碼
  1. public MyScript myScript;  
  2. void OnMouseOver () {  
  3.   myScript.NotifyMouseOver();  
  4. }  
public MyScript myScript;
void OnMouseOver () {
  myScript.NotifyMouseOver();
}




這樣作沒問題,可是不夠好。由於你須要一直保持另一個對象的引用,若是想通知多個對象要保持多個引用。代碼會變得很亂。

Messages 消息

另外一個辦法是用SendMessage或SendMessageUpwards方法。看上去這是解決問題的最好辦法,可是這些方法存在嚴重的缺陷,以個人觀點,你應該儘可能不去使用它們。

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

Events 事件

幸運的是有一個更好的解決辦法,這就是C#內置的事件機制。我不在這裏過多的描述機制是如何工做的,你若是有興趣能夠學習相關的知識,訪問MSDN手冊。(譯者推薦另一篇文章,C# 中的委託和事件

如今讓咱們看看如何在Unity3D中使用事件機制。

學習

C#代碼 複製代碼  收藏代碼
  1. using UnityEngine;  
  2. public class EventDispatcher : MonoBehaviour {  
  3.   public delegate void EventHandler(GameObject e);  
  4.   public event EventHandler MouseOver;  
  5.   void OnMouseOver () {  
  6.     if (MouseOver != null)  
  7.         MouseOver (this.gameObject);  
  8.   }  
  9. }  
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

C#代碼 複製代碼  收藏代碼
  1. private GameObject s;  
  2. [...]  
  3. s.GetComponent<EventDispatcher>().MouseOver += Listener;  
  4. [...]  
  5. void Listener(GameObject g) {  
  6.    // g is being hovered, do something...  
  7. }  
private GameObject s;
[...]
s.GetComponent<EventDispatcher>().MouseOver += Listener;
[...]
void Listener(GameObject g) {
   // g is being hovered, do something...
}



這種方式比用消息更靈活,由於它能夠被用在任何一個腳本中,而不只僅在同一個對象附屬關係中。若是在整個應用中保持一個單例模式的對象,你就能夠監放任何從這個對象分發出來的事件。

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

References, controllers and MVC

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

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

事件機制中,是由監聽者本身決定監聽什麼事件,何時開始監聽,何時中止監聽。像這樣的對象一般用於管理程序的狀態或者執行某些遊戲邏輯。這個就叫作控制器,借用MVC設計模式的概念。這樣咱們的代碼會更清晰,不易出錯。(譯者認爲觀察者設計模式更符合)

最後一點,我喜歡重載「+=」操做符去添加監聽方法。如今你也許可以猜到,若是想結束監聽某個事件,能夠這麼寫:

this

C#代碼 複製代碼  收藏代碼
  1. s.GetComponent<EventDispatcher>().MouseOver -= Listener;  
s.GetComponent<EventDispatcher>().MouseOver -= Listener;



固然你能夠建立一個通用的EventDispatcher類,實現全部GameObject可以分發的事件。能夠參看下面的代碼。另外在實現OnGUI事件時要特別當心,若是想知道爲何,讀讀這篇文章

spa

C#代碼 複製代碼  收藏代碼
    1. using UnityEngine;  
    2. using System.Collections;  
    3.   
    4. /**  
    5.  *  A simple event dispatcher - allows to listen to events in one GameObject from another GameObject 
    6.  * 
    7.  *  Author: Bartek Drozdz (bartek [at] everyday3d [dot] com) 
    8.  * 
    9.  *  Usage: 
    10.  *  Add this script to the object that is supposed to dispatch events.  
    11.  *  In another objects follow this pattern to register as listener at intercept events: 
    12.   
    13.     void Start () { 
    14.         EventDispatcher ev = GameObject.Find("someObject").GetComponent<EventDispatcher>(); 
    15.         ev.MouseDown += ListeningFunction; // Register the listener (and experience the beauty of overloaded operators!) 
    16.     } 
    17.  
    18.     void ListeningFunction (GameObject e) { 
    19.         e.transform.Rotate(20, 0, 0); // 'e' is the game object that dispatched the event 
    20.         e.GetComponent<EventDispatcher>().MouseDown -= ListeningFunction; // Remove the listener 
    21.     } 
    22.      
    23.  *  This class does not implement all standards events, nor does it allow dispatching custom events,  
    24.  *  but you shold have no problem adding all the other methods. 
    25.  */  
    26. public class EventDispatcher : MonoBehaviour  
    27. {  
    28.   
    29.     public delegate void EventHandler (GameObject e);  
    30.     public delegate void CollisionHandler (GameObject e, Collision c);  
    31.   
    32.     public event EventHandler MouseOver;  
    33.     void OnMouseOver ()  
    34.     {  
    35.         if (MouseOver != null)  
    36.             MouseOver (this.gameObject);  
    37.     }  
    38.   
    39.     public event EventHandler MouseDown;  
    40.     void OnMouseDown ()  
    41.     {  
    42.         if (MouseDown != null)  
    43.             MouseDown (this.gameObject);  
    44.     }  
    45.   
    46.     public event EventHandler MouseEnter;  
    47.     void OnMouseEnter ()  
    48.     {  
    49.         if (MouseEnter != null)  
    50.             MouseEnter (this.gameObject);  
    51.     }  
    52.   
    53.   
    54.     public event EventHandler MouseExit;  
    55.     void OnMouseExit ()  
    56.     {  
    57.         if (MouseExit != null)  
    58.             MouseExit (this.gameObject);  
    59.     }  
    60.   
    61.     public event EventHandler BecameVisible;  
    62.     void OnBecameVisible ()  
    63.     {  
    64.         if (BecameVisible != null)  
    65.             BecameVisible (this.gameObject);  
    66.     }  
    67.   
    68.     public event EventHandler BecameInvisible;  
    69.     void OnBecameInvisible ()  
    70.     {  
    71.         if (BecameInvisible != null)  
    72.             BecameInvisible (this.gameObject);  
    73.     }  
    74.   
    75.     public event CollisionHandler CollisionEnter;  
    76.     void OnCollisionEnter (Collision c)  
    77.     {  
    78.         if (CollisionEnter != null)  
    79.             CollisionEnter (this.gameObject, c);  
    80.     }  
    81.   
    82.     public event CollisionHandler CollisionExit;  
    83.     void OnCollisionExit (Collision c)  
    84.     {  
    85.         if (CollisionExit != null)  
    86.             CollisionExit (this.gameObject, c);  
    87.     }  
    88.       
    89. }  
相關文章
相關標籤/搜索