亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        游戲引擎中事件和響應機制的研究

        2022-05-30 12:16:27江紅偉米洪
        電腦知識與技術(shù) 2022年22期
        關(guān)鍵詞:事件委托

        江紅偉 米洪

        摘要:文章建立在抽象思維的基礎上,對觀察者模式進行了系統(tǒng)研究,并將這種良好的思維模式應用于實際的游戲引擎開發(fā)實例中。闡述了基于觀察者模式的委托、事件、消息傳遞與響應的關(guān)系,實現(xiàn)程序中各類對象之間協(xié)同工作,弱化具體類之間的耦合關(guān)系,使得某些相互有聯(lián)系的對象間不需要依賴對方而實現(xiàn)必要的通信與交互。最后在Unity3D中通過具體實例講述了回調(diào)系統(tǒng)內(nèi)置事件方法,以及.net泛型委托方法之間的差異,并通過事件傳遞消息的方法給出了游戲引擎開發(fā)中對象間完全解耦的解決方案。

        關(guān)鍵詞:觀察者模式;委托;事件;消息傳遞

        中圖分類號:TP311.1? ? ? 文獻標識碼:A

        文章編號:1009-3044(2022)22-0083-04

        1 引言

        Unity3D是現(xiàn)在主流的3D游戲引擎,它支持多種面向?qū)ο蟮恼Z言,其中C#語言的應用最為廣泛,是典型的游戲開發(fā)和虛擬現(xiàn)實開發(fā)的代表。在一個游戲系統(tǒng)的設計中,事件和響應機制的應用是不可回避的技術(shù)。在開發(fā)中,什么時候回調(diào)引擎基類MonoBehavior的事件、什么時候使用.net平臺的事件系統(tǒng),是編程邏輯時刻需要解決的問題。

        本文重點探討和研究C#語言中基于觀察者模式的事件和響應機制。一方面要盡量實現(xiàn)類的單一性原則,即一個類應該專注于做一件事情;另一方面,應該把程序中不變的部分分離出來形成抽象層、變動的部分形成各個不關(guān)聯(lián)的具體執(zhí)行層、減少耦合度,以達到程序中對象間的“低耦合、高內(nèi)聚”的目標。

        2 接口和抽象類

        在面向?qū)ο蟮母拍钪校械膶ο蠖际峭ㄟ^類來描繪的,但是反過來并不是所有的類都是用來描繪對象的。如果一個類中沒有包含具體的實現(xiàn),這樣的類就是抽象類。抽象類基本是用來表征在對問題領(lǐng)域進行分析、設計中得出的抽象概念,是對一系列看上去不同,但是本質(zhì)上相同的具體概念的抽象[1]。一般來說,可以構(gòu)造出一個固定的具有一組行為的抽象描述,但是這組行為卻能夠擁有任意多個可能的具體實現(xiàn)。這個抽象的描述就是抽象類,而這一組任意多個可能的具體實現(xiàn)則表現(xiàn)為所有可能的派生類。接口可以被看作是抽象類的變體,接口中所有的方法都是抽象的,可以通過接口來間接地實現(xiàn)多重繼承。

        接口和抽象類是面向?qū)ο缶幊痰幕?,賦予了C#語言強大的面向?qū)ο蟮哪芰?,為類的封裝、繼承和多態(tài)提供良好的設計基礎。在程序設計中,各類對象協(xié)同工作,對象之間通過封裝和繼承等方式進行通信,良好的協(xié)同工作是需要以接口和抽象類為基礎而展開的。

        根據(jù)里氏代換原則,派生類可以實現(xiàn)基類的抽象方法,而且基類應該不需要知道派生類的具體行為。接口和抽象類都屬于基類,是對業(yè)務邏輯的抽象,將派生類的方法進行了一定的規(guī)范,具體的實現(xiàn)是由派生類來完成的。當需求有變化時,只需建立新的子類來實現(xiàn)接口或抽象類就可以擴展出新的功能,而原有的依賴于該基類的派生類則不需要修改,做到了程序間的“低耦合”性,甚至“無耦合”性。這樣即可實現(xiàn)“開閉原則”,使得程序具有較好的擴展性的同時,又可以實現(xiàn)對子類修改的關(guān)閉,從而避免了程序員各自寫的子類之間的相互影響,減少子類修改對于整個系統(tǒng)帶來的影響。

        總之,接口和抽象類都是對客戶端程序的一種承諾,應該做到相對不變[2]。同時根據(jù)接口隔離原則,接口應當為客戶提供盡可能小的規(guī)范[3],以明確地區(qū)分各個接口和抽象類的分工和特征。

        3 觀察者模式

        在一個軟件系統(tǒng)中,各類對象之間是協(xié)同工作的,對象之間可以通過繼承、實例等進行消息傳遞。假如一個對象的行為發(fā)生變化時,和這個對象有關(guān)聯(lián)的其他對象都需要在程序結(jié)構(gòu)上進行重寫,則這種情況就導致了對象之間的完全依賴、形成緊密的耦合。所以好的設計模式需要借以優(yōu)秀的,可以方便開發(fā)者復用的程序設計模式[4]來構(gòu)建良好的程序關(guān)系。

        “觀察者模式(Observer mode) ”或稱“發(fā)布-訂閱模式(Publish/Subscribe) ”屬于設計模式中的行為模型,可以弱化具體類之間的耦合關(guān)系,使得某些相互有聯(lián)系的對象間不需要依賴對方而實現(xiàn)必要的通信與交互。該模式定義了對象間的一對多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象[5]。這個目標對象在狀態(tài)發(fā)生變化時,會通知所有的觀察者對象,使它們能夠自動更新[6]。且主題發(fā)出通知并不需要知道具體的觀察者對象,觀察者之間也不需要知道其他觀察者的存在。觀察者模式在降低程序間耦合度的同時能夠維持好對象間行動的一致性,保證程序間的高度協(xié)作性。

        如圖1所示:Subject類(目標類,是廣播者) 一般被定義為抽象類,其中保存了Observer類(觀察者類,是訂閱者) 的集合,所以可以讓多個觀察者同時監(jiān)聽該目標。此外它提供給ConcreteSubject類(具體目標類) 需要實現(xiàn)的抽象方法,以規(guī)范目標類中具體方法的實現(xiàn)。

        Observer類(觀察者類) 一般定義為接口,可以使得多個ConcreteObserver類(具體觀察者類) 繼承于該接口。這樣使得各個具體觀察者對象可以被保存在Subject類的Observer集合中,使得具體目標類可以遍歷到各個具體觀察者對象,并對其發(fā)送通知,實現(xiàn)多播效果。最后具體觀察者接收到通知并執(zhí)行各自的實現(xiàn)方法。

        觀察者模式實現(xiàn)了目標類與觀察者類之間的抽象耦合,目標類只需要保存觀察者對象的引用,并不需要知道具體觀察者是誰,具體觀察者只需遵守接口的約定即可。通過傳統(tǒng)的抽象類和接口的方式可以實現(xiàn)該模式,也可以通過.net框架中的委托(Delegates) 與事件(Event) 機制來實現(xiàn),相對來說委托和事件機制能進一步弱化目標類和觀察者類之間的依賴關(guān)系[7]。

        4 委托與事件

        所有的委托類型都派生于基類System.Delegate。使用委托的時候,廣播者類包含一個委托字段,廣播者通過調(diào)用委托來決定什么時候進行廣播;觀察者類(訂閱者類) 是方法目標的接收者,通過在委托上調(diào)用“+=”開始進行監(jiān)聽、調(diào)用“-=”結(jié)束監(jiān)聽[7];一個訂閱者不需要知道也不會干擾其他的訂閱者,以實現(xiàn)訂閱者之間的解耦。

        實際上,委托是不可變的,使用“+=”或“-=”操作符時,其實是創(chuàng)建了新的委托實例,并把它賦值給了當前的委托變量,初始狀態(tài)時這個委托變量可以是null,如圖2所示。

        在實際的使用中,泛型委托可以提供了更好的便捷性,下面的程序中定義了一個返回類型和傳參類型都為Object的泛型委托,并且創(chuàng)建了一個發(fā)布者類,聲明了這個泛型委托的實例對象OnPublisher,利用發(fā)布方法。

        Publish調(diào)用了該對象委托的方法,并使用一個公有字段“output”接收了該委托的返回值,如圖3所示。

        委托的方法便是觀察者類的具體實現(xiàn)方法,當然可以同時委托多個觀察者以不同的實現(xiàn)方法,只需這些方法的簽名和委托類的約定一致就行[8],這里示例了一個觀察者類的實現(xiàn)方法,用來傳入一個整數(shù)并返回一個整型數(shù)值,如圖4所示。

        最后在客戶端程序中將觀察者的實現(xiàn)方法賦值給委托對象,調(diào)用委托的發(fā)布方法并進行傳值。這樣幾個程序間相互獨立、各司其職,發(fā)布者和觀察者之間解耦了,如圖5所示。

        .net3.5以后還提供了兩類通用的delegates,如果方法有返回值,則使用Func或者Func<>;如果方法沒有返回值,則使用Action或者Action<>。所以上例中的泛型委托就可以直接由Func<>創(chuàng)建委托對象了,而不用首先定義委托。<>中左側(cè)是傳參類型、右側(cè)是返回值類型,最多可以有16個傳參,如圖6所示。

        本例打印輸出結(jié)果為“9”。如果使用不帶返回值的泛型委托得到同樣的輸出結(jié)果,則可以使用Action<>型委托,<>中定義的是傳值類型,最多也可以有16個。重構(gòu)這三個類,如圖7、圖8、圖9所示。

        事件(event) 是對委托進一步封裝的結(jié)果,讓委托只暴露特定的部分子集,防止訂閱者之間相互干擾,可以安全地實現(xiàn)廣播者/訂閱者模式[8]。在Subject類外,只能通過“+=”和“-=”來注冊和注銷事件、即事件訪問器只能通過“+=” 和“-=”來實現(xiàn)。只需引入“event”關(guān)鍵字就可以將委托封裝為事件。如上例,將“public static Action OnPublisher”改為“public static event Action OnPublisher”,即可。

        5 Unity3D的事件和響應

        在Unity3D中為了響應一個GameObject的事件分發(fā),常規(guī)的做法是回調(diào)系統(tǒng)相關(guān)的內(nèi)置事件。而MonoBehaviour是Unity中所有腳本的基類,使用C#需要顯式的從MonoBehaviour繼承系統(tǒng)內(nèi)置的事件[9]。

        為了講述事件與響應的關(guān)系,在場景制作了3個三維物體作為按鈕對象,并給它們配置好Collider碰撞組件。設計目標是讓鼠標和這3個按鈕對象之間產(chǎn)生互動效果,如圖10所示。

        建立一個類繼承自MonoBehaviour基類,分別回調(diào)鼠標滑過事件(OnMouseOver) 、鼠標退出事件(OnMouseExit) 。

        Unity3D中繼承自MonoBehaviour的類形成實例的方法是將它作為組件掛載給游戲?qū)ο骩9],所以這3個按鈕對象必須分別掛載這個繼承類。

        這種做法雖然很輕松就實現(xiàn)了這3個三維按鈕的鼠標交互效果,但是這種程序結(jié)構(gòu)很不友好。誰是廣播者、誰是訂閱者似乎無法分辨。程序所有的功能都被寫在了這一個類里,全耦合且毫無內(nèi)聚性可言,這樣導致項目幾乎沒有擴展與維護的可能性,牽一發(fā)則動全身。而且程序是被掛載到場景中的每個游戲?qū)ο笊系?,后期想要修改游戲?qū)ο蟮男袨椋瑒t要人工地逐個去檢查、修改每一個對象的各個實現(xiàn)方法。當場景變大后,這個修改工作將是龐大而煩瑣的過程。這個回調(diào)系統(tǒng)事件類的基本樣式如圖11所示。

        上文中已經(jīng)探討了“發(fā)布-訂閱”模式的優(yōu)勢,再借助.net事件機制,完全可以設計出較為優(yōu)秀的程序結(jié)構(gòu)?;驹硎牵簩懸粋€發(fā)布者類,利用場景中的主攝像機發(fā)出射線與場景中的Collider組件對象發(fā)生碰撞,這種碰撞有三種狀態(tài),分別是射線進入某對象、停留在某對象上和離開某對象;程序設計上可以把這個三個狀態(tài)分別定義為三個事件OnRayEnter、OnRayStay、OnRayExit,而這些事件只需要傳遞參數(shù)、不需要有返回值,所以可以使用Action<>型的委托事件來實現(xiàn);傳遞的參數(shù)就是由不同的事件而捕捉到的射線碰撞對象,然后由一個觀察者類來接收這些事件所傳遞出來的參數(shù),根據(jù)傳遞過來的不同的Collider組件對象來做一些具體的事務。

        因為發(fā)布者類和觀察者類都不需要回調(diào)Unity3D系統(tǒng)的內(nèi)置事件,所以它們是無須繼承系統(tǒng)基類的,這樣也就無需將這兩個類掛載給場景對象來形成實例。只需在客戶端程序?qū)@兩個類實例化后,直接將對應的觀察者方法注冊給發(fā)布者的對應事件,就建立了發(fā)布者事件和觀察者實現(xiàn)方法之間的聯(lián)系。發(fā)布者類和觀察者類的基本樣式如圖12、圖13所示。

        而客戶端程序仍然需要繼承MonoBehaviour基類,因為它必須回調(diào)Unity3D系統(tǒng)的Start()和Update()函數(shù)。在Start()函數(shù)中對事件進行注冊,在Update()函數(shù)中回調(diào)發(fā)布者類的射線碰撞方法,并進行事件發(fā)布??蛻舳祟惖幕緲邮饺鐖D14所示。

        最后將這個客戶端類掛載給場景中的一個空物體上,讓它形成一個實例。前例中,程序都被分散地掛載在各個游戲?qū)ο笊?,導致管理上非常混亂。而這里,場景中的資源被這個“空物體”統(tǒng)一化處理了,規(guī)范化了場景資源的管理。

        利用.net事件機制,發(fā)布者和觀察者之間一定程度上實現(xiàn)了解耦,發(fā)布者和觀察者各自只做自己該做的事、實現(xiàn)了高內(nèi)聚,也大大方便了項目后期的增、刪、改、查。

        6 Unity3D中為事件傳遞消息

        上文已經(jīng)實現(xiàn)了發(fā)布者模式的程序結(jié)構(gòu),但是還需要繼續(xù)完善一下,因為發(fā)布者還是需要傳參給觀察者的,導致這兩個類之間沒有完全解耦。這時,可以考慮事件的消息傳遞機制,以達到完全解耦的目標。

        .net平臺中定義了一個基類EventArgs專門用來為事件傳遞消息。還定義了一個泛型委托EventHandler,可傳遞兩個參數(shù),第一個類型是Object類,是發(fā)布者;第二類型便是EventArgs類,是消息傳遞類[8]。

        所以,可以建立一個繼承EventArgs的類,專門用作事件的消息傳遞,作為發(fā)布者類和觀察者類之間信息傳遞的橋梁。這樣觀察者就不必知道發(fā)布者所傳遞的是什么了,便可實現(xiàn)發(fā)布者和觀察者之間的完全解耦。

        首先創(chuàng)建一個繼承EventArgs基類的消息傳遞類PublisherEventArgs,其中定義兩個公開的屬性,分別是上一幀的碰撞信息、當前幀的碰撞信息。并通過構(gòu)造函數(shù)對相應屬性進行賦值,如圖15所示。

        發(fā)布者類中,重新定義三個事件為EventHandler型委托事件,并創(chuàng)建消息傳遞類PublisherEventArg的變量e,如圖16所示。

        對于事件的發(fā)布方法CollisionProcess(),首先對變量e進行實例化“e = new PublisherEventArgs(colliderOld, current)”;事件發(fā)布器的參數(shù)改為泛型委托EventHandler所規(guī)定的兩類型參數(shù)“OnRayExit?.Invoke(this, e)”、“OnRayStay?.Invoke(this, e)”、“OnRayExit?.Invoke(this, e)”。這樣就可以讓相應事件發(fā)生時的碰撞對象參數(shù)傳遞給PublisherEventArgs類的構(gòu)造函數(shù)。

        游戲運行過程中,每幀都會傳兩個參數(shù)過去,第一個參數(shù)是上一幀的碰撞信息、第二個是當前幀的碰撞信息。這樣就無需像上例那樣,針對不同的事件發(fā)布器還要人為判斷所傳參數(shù)到底是上一幀的碰撞對象還是當前幀的,避免了人工判斷可能導致的失誤。

        此時,觀察者就完全與發(fā)布者無關(guān)了,只與消息傳遞類PublisherEventArgs有關(guān)系,其實現(xiàn)方法RayInputIn()和RayInputOut()寫法大致如圖17所示。

        最后客戶端程序也變得簡單了,只需對事件進行注冊,無須考慮事件要傳什么參數(shù)給委托方法。因為事件根本就沒有傳參給所委托的方法,而是直接傳給了消息傳遞類,這時發(fā)布者和觀察者之間就完全解耦了。事件注冊寫法如“publisherSubject.OnRayEnter += observerObject.RayInputIn”,其他兩個事件的注冊和此寫法類同。

        7 結(jié)束語

        游戲引擎的事件和響應的機制,歸根究底還是要從面向?qū)ο蟮母境霭l(fā),帶著抽象思維去思考問題;從軟件設計模式出發(fā),借助優(yōu)秀的、可以方便開發(fā)者復用的程序設計模式來構(gòu)建良好的程序關(guān)系。

        基于觀察者模式的程序設計思維,利用好委托、事件和消息傳遞機制,這些對于實現(xiàn)觀察者模式有的獨到的支撐和便利的工具,來設計和優(yōu)化Unity3D的程序模塊,可以很好地做到對象間“低耦合”甚至“無耦合”的目標。

        參考文獻:

        [1] 曹步清,金甌.Java中的Abstract Class與Interface技術(shù)研究[J].計算機技術(shù)與發(fā)展,2006,16(8):110-112,115.

        [2] 耿祥義,張躍平.Java設計模式[M].北京:清華大學出版社,2009:132-134.

        [3] 李航.基于MDA的BSS計費系統(tǒng)設計與實現(xiàn)[D].哈爾濱:哈爾濱工業(yè)大學,2012:24.

        [4] Gamma E,Helm R,Johns R,et al.Design patterns: elements of reusable object-oriented software[M].Beijing:China Machine Press,2002.

        [5] 孟婷婷,何利力.Observer設計模式在手機導航軟件中的應用[J].電腦知識與技術(shù),2014,10(19):4579-4582.

        [6] Gamma E,Helm R,Johns R.設計模式:可復用面向?qū)ο筌浖幕A[M].李英軍,馬曉星,蔡敏,等譯.北京:機械工業(yè)出版社,2005:89.

        [7] 吳清壽.基于事件機制的觀察者模式及應用[J].重慶理工大學學報(自然科學版),2012,26(9):100-104.

        [8] 微軟公司.基于C#的.NET Framework程序設計[M].北京:高等教育出版社,2004:149-152.

        [9] 楊秀杰,楊麗芳.虛擬現(xiàn)實(VR)交互程序設計[M].北京:中國水利水電出版社,2019:34-38.

        【通聯(lián)編輯:謝媛媛】

        猜你喜歡
        事件委托
        建設項目合同事項受托回避與合并委托問題探討
        Contiki系統(tǒng)進程與事件剖析
        網(wǎng)絡輿情反轉(zhuǎn)現(xiàn)象中的“參照點效應”
        新聞界(2016年7期)2016-12-23 14:56:11
        保羅·利科的“話語事件”思想
        授之以魚,不如授之以漁
        考試周刊(2016年92期)2016-12-08 00:10:56
        熱點事件中的“輿論搭車”現(xiàn)象探析
        新聞前哨(2016年11期)2016-12-07 11:25:41
        績效評價在委托管理酒店中的應用
        新聞前哨(2016年1期)2016-12-01 06:18:04
        嵌入式系統(tǒng)課程“中斷、異常與事件”教學實踐及啟示
        計算機教育(2016年7期)2016-11-10 08:31:34
        治理現(xiàn)代化:委托制下的權(quán)力清單制
        招標代理中的授權(quán)委托——以案說法
        亚洲精品在线国产精品| 国产999精品久久久久久| 狠狠色狠狠色综合日日92| 日日摸日日碰人妻无码老牲| 久久精品国产精品亚洲婷婷| 国产剧情亚洲一区二区三区| 久久久久亚洲av无码a片| 亚洲性爱视频| 大地资源网最新在线播放| 亚洲中文字幕无码卡通动漫野外 | 色综久久综合桃花网国产精品| 伊人青青草综合在线视频免费播放 | 经典三级免费看片天堂| 奇米影视第四色首页| 国产欧美VA欧美VA香蕉在| 国产不卡一区二区三区视频| 亚洲中文字幕久久精品色老板| 久久亚洲av无码西西人体| 天天天综合网| 春色成人在线一区av| 国产精品丝袜美女久久| 久久一区二区三区久久久| 性xxxx18免费观看视频| 精品国产网红福利在线观看| 国产黑色丝袜在线观看网站91| 中文字幕亚洲精品久久| 手机福利视频| 国产成人精品日本亚洲18| 亚洲精品久久久中文字 | 日本丰满少妇裸体自慰| 欧美 国产 日产 韩国 在线 | 成av人大片免费看的网站| 高清偷自拍亚洲精品三区| 国内久久婷婷激情五月天| 亚洲av免费高清不卡| 白嫩丰满少妇av一区二区| 亚洲精品成人区在线观看| 国产成人aa在线观看视频| av天堂在线免费播放| 狠狠躁夜夜躁人人爽超碰97香蕉 | av鲁丝一区鲁丝二区|