? ?玩家在游戲戰(zhàn)斗過程中,被某個怪物把血打掉了,然后界面上的UI血條的數(shù)值就要發(fā)生變化,

按照一般的處理方式,我們在戰(zhàn)斗的邏輯里面掉血后,要去找到UI界面中的某個節(jié)點(diǎn),把數(shù)值給它設(shè)置好。一個查找還好,要是多個地方需要改變數(shù)值,多個節(jié)點(diǎn)都需要被改變,玩家戰(zhàn)斗邏輯,不可能每個節(jié)點(diǎn)都去找一下,然后設(shè)置一下,這樣也不靈活,也不方便擴(kuò)展。這樣就相當(dāng)于玩家Player對象與要更新顯示的這些對象耦合在一起了。所以就需要編寫一個模塊來解耦,這個模塊就是事件訂閱與發(fā)布模塊。針對上面的情況,如何解耦呢?
我們引入一個事件監(jiān)聽與事件觸發(fā)機(jī)制,當(dāng)我們某個邏輯對某個事件感興趣的時候,我們就向事件訂閱與發(fā)布模塊發(fā)起一個監(jiān)聽,這樣事件訂閱與發(fā)布模塊保留了監(jiān)聽者的回掉函數(shù)。當(dāng)玩家在戰(zhàn)斗中掉血了以后,只要通過事件與訂閱模塊,派送一個事件就可以了,事件訂閱模塊內(nèi)部,看哪些對這個事件感興趣,分別回調(diào),這樣玩家不用去關(guān)心當(dāng)前多少人對這個事件感興趣。而對事件感興趣的監(jiān)聽者也不用關(guān)心是誰觸發(fā)的,這樣它們之間就解耦了。
事件訂閱與發(fā)布模塊具體如何設(shè)計(jì)呢?新建一個單例事件管理組件類EventMgr,這個單例組件實(shí)例化到GameApp節(jié)點(diǎn)上。如圖1.7-2

(圖1.7-2)
events_map是一個表,存放了哪些監(jiān)聽者對哪些事件感興趣。當(dāng)events_map里面有數(shù)據(jù)的時候,數(shù)據(jù)結(jié)構(gòu)如下: { “事件名字1”: [監(jiān)聽者1{caller, func}, 監(jiān)聽者2], “事件名字2”: []}
events_map數(shù)據(jù)結(jié)構(gòu)中,基于事件名字的key, value是一個數(shù)組對象,存放了對這個事件感興趣的所有的監(jiān)聽者,每個監(jiān)聽者,保存一個caller與func,當(dāng)事件觸發(fā)func調(diào)用時,func中的this為caller。
接下來我們來看下提供給監(jiān)聽者的監(jiān)聽事件接口如圖1.7.3:

(圖1..7-3)
當(dāng)我們要監(jiān)聽某個事件的時候,傳入事件的名字,回調(diào)時的this對象(caller), 回調(diào)函數(shù)func。
先判斷一下有沒有其它的人在監(jiān)聽,eventName這個key是否為null, 如果為null就創(chuàng)建一個數(shù)組對象用來保存所有的監(jiān)聽者。然后再push一個監(jiān)聽者對象到數(shù)組里面,對應(yīng)的結(jié)構(gòu)為 {caller: xxxxx, func: xxxxx}。
接下來我們來看下如何派送發(fā)布一個事件,如圖1.7.4:

(圖1..7-4)
當(dāng)要派送發(fā)布一個事件的時候,調(diào)用Emit函數(shù),eventName為事件的名字,udata為派送的數(shù)據(jù)。先判斷一下是否有這個事件的監(jiān)聽隊(duì)列,如果沒有,直接返回。遍歷事件隊(duì)列數(shù)組里面的每個監(jiān)聽對象,顯示的傳遞caller作為函數(shù)的this, 把udata傳遞給回調(diào)函數(shù)。這樣監(jiān)聽者的事件回調(diào)函數(shù)就會被調(diào)用。
今天的教程分享就到這里,具體代碼可以閱讀EventMgr.ts,把代碼閱讀一遍,完整的掌握事件訂閱與發(fā)布模塊的編寫與實(shí)現(xiàn)。