Unity5.x EventSystem事件系統(tǒng)

一、EventSystem對(duì)象的說明

當(dāng)我們?cè)趫?chǎng)景中創(chuàng)建任一UI對(duì)象后,Hierarchy面板中都可以看到系統(tǒng)自動(dòng)創(chuàng)建了對(duì)象EventSystem,可以看到該對(duì)象下有三個(gè)組件:EventSystem、StandaloneInputModule、TouchInputModule,后面兩個(gè)組件都繼承自BaseInputModule。
EventSystem組件主要負(fù)責(zé)處理輸入、射線投射以及發(fā)送事件。一個(gè)場(chǎng)景中只能有一個(gè)EventSystem組件,并且需要BaseInputModule類型組件的協(xié)助才能工作。EventSystem在一開始的時(shí)候會(huì)把自己所屬對(duì)象下的BaseInputModule類型組件加到一個(gè)內(nèi)部列表,并且在每個(gè)Update周期通過接口UpdateModules接口調(diào)用這些基本輸入模塊的UpdateModule接口,然后BaseInputModule會(huì)在UpdateModule接口中將自己的狀態(tài)修改成'Updated',之后BaseInputModule的Process接口才會(huì)被調(diào)用。
BaseInputModule是一個(gè)基類模塊,負(fù)責(zé)發(fā)送輸入事件(點(diǎn)擊、拖拽、選中等)到具體對(duì)象。EventSystem下的所有輸入模塊都必須繼承自BaseInputModule組件。StandaloneInputModule和TouchInputModule組件是系統(tǒng)提供的標(biāo)準(zhǔn)輸入模塊和觸摸輸入模塊,我們可以通過繼承BaseInputModule實(shí)現(xiàn)自己的輸入模塊。
除了以上兩個(gè)組件,還有一個(gè)很重要的組件通過EventSystem對(duì)象我們看不到,它是BaseRaycaster組件。BaseRaycaster也是一個(gè)基類,前面說的輸入模塊要檢測(cè)到鼠標(biāo)事件必須有射線投射組件才能確定目標(biāo)對(duì)象。系統(tǒng)實(shí)現(xiàn)的射線投射類組件有PhysicsRaycaster, Physics2DRaycaster, GraphicRaycaster。這個(gè)模塊也是可以自己繼承BaseRaycaster實(shí)現(xiàn)個(gè)性化定制。
總的來說,EventSystem負(fù)責(zé)管理,BaseInputModule負(fù)責(zé)輸入,BaseRaycaster負(fù)責(zé)確定目標(biāo)對(duì)象,目標(biāo)對(duì)象負(fù)責(zé)接收事件并處理,然后一個(gè)完整的事件系統(tǒng)就有了。
另外,其實(shí)這些說明官方都有提供,這里也就是把英文譯成了中文,并整理下,加上自己的理解,有問題的地方請(qǐng)各路神仙多多指教。
官方文檔在這里: http://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.html

二、UGUI中的事件系統(tǒng)

根據(jù)第一節(jié)中的說明,EventSystem和BaseInputModule是粘在一個(gè)對(duì)象上的,這兩個(gè)模塊在EventSystem對(duì)象上可以直接看到。那么,BaseRaycaster模塊呢。。。
其實(shí)射線檢測(cè),肯定是從攝像機(jī)發(fā)起的,那么BaseRaycaster模塊也一定和攝像機(jī)關(guān)系一定不簡(jiǎn)單。
對(duì)于UI模塊,在Canvas對(duì)象下我們可以看到GraphicRaycaster組件。如果Canvas的渲染模式是SceenSpace-Overlay,那么我們是看不到Camera組件的。所以應(yīng)該是GraphicRaycaster會(huì)對(duì)UI不同的渲染模式做特殊處理。
因?yàn)橛蠫raphicRaycaster組件的原因,Canvas上的所有UI對(duì)象,都可以接受輸入模塊發(fā)出的事件,具體事件的處理在第四節(jié)說明。

三、場(chǎng)景對(duì)象中使用事件系統(tǒng)

場(chǎng)景中的非UI對(duì)象,如果想要接收輸入模塊的事件,一樣的道理,也需要給攝像機(jī)掛上一個(gè)射線檢測(cè)組件。PhysicsRaycaster, Physics2Draycaster這兩個(gè)組件分別是用于3D和2D的場(chǎng)景。當(dāng)然,還需要場(chǎng)景的對(duì)象掛了collider射線才檢測(cè)的到。
其實(shí)官方對(duì)射線檢測(cè)也是做了說明的,如果不詳讀手冊(cè)是不會(huì)發(fā)現(xiàn)的,這里是傳送門:
http://docs.unity3d.com/Manual/Raycasters.html
如果場(chǎng)景中只有一個(gè)射線檢測(cè)源:When a Raycaster is present and enabled in the scene it will be used by the EventSystem whenever a query is issued from an InputModule.
如果場(chǎng)景中有多個(gè)射線檢測(cè)源:If multiple Raycasters are used then they will all have casting happen against them and the results will be sorted based on distance to the elements.

四、響應(yīng)事件

1、輸入模塊可以檢測(cè)到的事件

     StandaloneInputModule和TouchInputModule兩個(gè)組件會(huì)檢測(cè)一些輸入操作,以事件的方式(message系統(tǒng))通知目標(biāo)對(duì)象,那么這兩個(gè)組件支持的事件主要有以下:

IPointerEnterHandler - OnPointerEnter - Called when a pointer enters the object
IPointerExitHandler - OnPointerExit - Called when a pointer exits the object
IPointerDownHandler - OnPointerDown - Called when a pointer is pressed on the object
IPointerUpHandler - OnPointerUp - Called when a pointer is released (called on the original the pressed object)
IPointerClickHandler - OnPointerClick - Called when a pointer is pressed and released on the same object
IInitializePotentialDragHandler - OnInitializePotentialDrag - Called when a drag target is found, can be used to initialise values
IBeginDragHandler - OnBeginDrag - Called on the drag object when dragging is about to begin
IDragHandler - OnDrag - Called on the drag object when a drag is happening
IEndDragHandler - OnEndDrag - Called on the drag object when a drag finishes
IDropHandler - OnDrop - Called on the object where a drag finishes
IScrollHandler - OnScroll - Called when a mouse wheel scrolls
IUpdateSelectedHandler - OnUpdateSelected - Called on the selected object each tick
ISelectHandler - OnSelect - Called when the object becomes the selected object
IDeselectHandler - OnDeselect - Called on the selected object becomes deselected
IMoveHandler - OnMove - Called when a move event occurs (left, right, up, down, ect)
ISubmitHandler - OnSubmit - Called when the submit button is pressed
ICancelHandler - OnCancel - Called when the cancel button is pressed
只要目標(biāo)對(duì)象的mono腳本實(shí)現(xiàn)了以上接口,那么輸入模塊會(huì)將檢測(cè)到的事件通過這些接口通知給目標(biāo)對(duì)象。
參考:http://docs.unity3d.com/Manual/SupportedEvents.html
如果你自定義了自己的輸入模塊,那么以上這些事件肯定是不能用的了。

2、接收輸入事件的方式

1)、自行繼承接口實(shí)現(xiàn)監(jiān)聽

     在mono腳本中繼承輸入模塊提供的事件接口,如下圖。接口的定義方式也可以查下官方手冊(cè),http://docs.unity3d.com/ScriptReference/EventSystems.IBeginDragHandler.html 這邊有每一個(gè)接口的定義方式,放心大膽地點(diǎn)進(jìn)去。另外,添加ObjChooseEvent組件的對(duì)象,一定要有Collider哦。
using UnityEngine;  
using UnityEngine.EventSystems;  
  
public class Player3DShow : MonoBehaviour,IDragHandler,IPointerUpHandler {  
  
    public void OnDrag(PointerEventData data){  
        Vector2 deltaPos = data.delta;  
          
    }  
    public void OnPointerUp (PointerEventData eventData)  
    {  
        Vector2 deltaPos = data.delta;  
    }  
  
}  

2)、 通過EventTrigger組件監(jiān)聽事件

     這是一個(gè)官方組件。在需要監(jiān)聽事件的對(duì)象上,掛上這個(gè)組件,然后在Inspector面板展開配置,你會(huì)看到這個(gè)組件提供了所有輸入模塊支持的事件類型的監(jiān)聽,如下圖。

這種方式的優(yōu)點(diǎn)是,當(dāng)你選中一個(gè)你要監(jiān)聽的類型,你可以為這個(gè)事件類型添加多個(gè)監(jiān)聽接口,統(tǒng)一管理,可以清楚的知道到底哪些地方響應(yīng)了這個(gè)事件呢。如果是繼承Interface的方式,它將會(huì)分散在N個(gè)腳本里,一旦出現(xiàn)問題,那查起來一定會(huì)很酸爽。
但是這種通過配置的方式,一旦項(xiàng)目多人協(xié)作,項(xiàng)目的復(fù)雜度起來,這種拖來拽去的配置終究是會(huì)有很多問題的,比如某個(gè)組件刪除,比如響應(yīng)接口改了個(gè)名字~~都會(huì)導(dǎo)致配置丟失,而問題又不能及時(shí)發(fā)現(xiàn)。又或者程序的監(jiān)聽接口因?yàn)槟承l件而不同。所以也許你會(huì)需要第三種方式。

3)、動(dòng)態(tài)添加EventTrigger組件或者修改組件

using System.Collections.Generic;  
using UnityEngine;  
using UnityEngine.Events;  
using UnityEngine.EventSystems;  
  
public class ScriptControl : MonoBehaviour {  
  
    // Use this for initialization  
    void Start ()  
    {  
        var trigger = transform.gameObject.GetComponent<EventTrigger>();  
        if (trigger == null)  
            trigger = transform.gameObject.AddComponent<EventTrigger>();  
  
        // 實(shí)例化delegates  
        trigger.triggers = new List<EventTrigger.Entry>();  
  
        // 定義需要綁定的事件類型。并設(shè)置回調(diào)函數(shù)  
        EventTrigger.Entry entry = new EventTrigger.Entry();  
        // 設(shè)置 事件類型  
        entry.eventID = EventTriggerType.PointerClick;  
        // 設(shè)置回調(diào)函數(shù)  
        entry.callback = new EventTrigger.TriggerEvent();  
        UnityAction<BaseEventData> callback = new UnityAction<BaseEventData>(OnScriptControll);  
        entry.callback.AddListener(callback);  
        // 添加事件觸發(fā)記錄到GameObject的事件觸發(fā)組件  
        trigger.delegates.Add(entry);  
    }  
  
    public void OnScriptControll(BaseEventData arg0)  
    {  
        Debug.Log("Test Click");  
    }  
  
}  

五、EventSystem組件提供的一些有意思的接口

其實(shí)文檔都有http://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.html 只是也許你沒有注意。
點(diǎn)擊EventSystem對(duì)象,你可以看到運(yùn)行時(shí)候的一些詳細(xì)數(shù)據(jù):

變量:

firstSelectedGameObject:這個(gè)值可以在面板設(shè)置,如果你需要游戲在啟動(dòng)的時(shí)候自動(dòng)選中某個(gè)對(duì)象,需要鼠標(biāo)的那一下點(diǎn)擊。
currentSelectedGameObject:當(dāng)前選中的對(duì)象,你可以通過這個(gè)值判斷當(dāng)前是否鼠標(biāo)點(diǎn)擊在對(duì)象上,因?yàn)橐苍S你有拖動(dòng)攝像機(jī)的功能,但是你又不喜歡點(diǎn)擊某些對(duì)象的時(shí)候這個(gè)功能又被響應(yīng),所以通過這個(gè)變量判斷是一個(gè)很好的辦法。

接口:

IsPointerOverGameObject:當(dāng)前鼠標(biāo)是否在事件系統(tǒng)可以檢測(cè)的對(duì)象上。

SetSelectedGameObject:這個(gè)接口也許你會(huì)忽略,但是它很棒。因?yàn)槟泓c(diǎn)擊場(chǎng)景對(duì)象的時(shí)候,如果不調(diào)用這個(gè)接口,你的對(duì)象是收不到OnSelect事件的,currentSelectedGameObject的值也不會(huì)被設(shè)置的,必須在點(diǎn)擊事件里調(diào)用這個(gè)接口設(shè)置選中對(duì)象!
Ex:

public void OnPointerClick(PointerEventData eventData)  
{  
         print ("OnPointerClick...");  
         currEvent.SetSelectedGameObject(gameObject);  
}  

不用在場(chǎng)景里找EventSystem對(duì)象,EventSystem組件有一個(gè)current靜態(tài)變量,它就是你要的對(duì)象,直接EventSystem.current即可使用。

六、點(diǎn)擊事件的特殊實(shí)現(xiàn)方式,使用Button控件

針對(duì)Click事件還存在一種特殊實(shí)現(xiàn)方式,在UGUI系統(tǒng)中,官方提供了一個(gè)Button控件,Button封裝了一個(gè)Onlick事件,操作方式類似于方式二,
使用Button我們可以實(shí)現(xiàn)動(dòng)態(tài)的變更鼠標(biāo)綁定的點(diǎn)擊事件。代碼如下:

using UnityEngine;  
using System.Collections;  
using UnityEngine.UI;  
  
public class BtnControl : MonoBehaviour {  
  
    // Use this for initialization  
    void Start ()  
    {  
        var button = transform.gameObject.GetComponent<Button>();  
        if (button != null)  
        {  
            button.onClick.RemoveAllListeners();  
            button.onClick.AddListener(TestClick);  
        }  
    }  
  
    public void TestClick()  
    {  
        Debug.Log("Test Click. This is Type 4");  
    }  
      
    // Update is called once per frame  
    void Update () {  
          
    }  
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,417評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,267評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,471評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,698評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評(píng)論 1 294
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,204評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,436評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容