cocos2dx 3.10 事件機(jī)制

cocos2dx的事件機(jī)制里存在三類:Event、EventListener、EventDispatcher
先理解一下它們之間的關(guān)系
當(dāng)我們按下按鈕時(shí)(Event),會(huì)觸發(fā)一個(gè)特定的事件(EventListener相當(dāng)于回調(diào)函數(shù)),而這個(gè)特定的事件又存儲(chǔ)在EventDispatcher里,可能按下這個(gè)按鈕會(huì)觸發(fā)多個(gè)事件芽突,而事件的先后就是靠EventDispatcher來決定的。

Event的相關(guān)類

Event(基類), EventCustom(自定義事件), EventTouch(觸摸事件), EventMouse(鼠標(biāo)事件), EventKeyboard(鍵盤事件), EventFocus(控件獲取焦點(diǎn)事件), EventAcceleration(加速計(jì)事件)


1. Event

1.1 事件是什么

當(dāng)出現(xiàn)來自鼠標(biāo)董瞻,鍵盤寞蚌,觸屏,搖桿等輸入源的輸入時(shí)钠糊,這個(gè)事實(shí)稱之為事件

1.2 cocos2dx是如何處理事件

引擎無時(shí)無刻都在感受事件挟秤。

在循環(huán)中每一幀會(huì)調(diào)用pollEvents來檢測外部事件,一旦有事件發(fā)生抄伍,就會(huì)調(diào)用EventDispatcher::dispatchEvent(Event* event)艘刚,來判斷事件是否需要處理,給誰處理的后續(xù)問題截珍。

1.3 源碼分析

Event

enum class Type    
   {    
       TOUCH,    
       KEYBOARD,    
       ACCELERATION,    
       MOUSE,    
       FOCUS,    
       CUSTOM    
   };    
   Type _type;     ///< Event type    
       
   bool _isStopped;       ///< whether the event has been stopped.    
   Node* _currentTarget;   

可以看出Event主要包含三個(gè)變量攀甚,一個(gè)是事件類型_type(也就是定義的枚舉類型:觸摸、鍵盤等)岗喉,isStopped判斷事件是否停止秋度,只要事件停止,其相關(guān)的Listener都要停止callback調(diào)用钱床。

EventTouch
它對應(yīng)于四種觸摸操作荚斯,不同的EventCode可以告訴Listener來調(diào)用不同的callback。

enum class EventCode    
   {    
       BEGAN,    
       MOVED,    
       ENDED,    
       CANCELLED    
   };    

EventCustom
它是用戶自定義事件,userData記錄用戶自定義數(shù)據(jù)事期,另一個(gè)eventName是用戶給事件取的別名

void* _userData;       ///< User data    
std::string _eventName;    

2. EventListener

std::function<void(Event*)> _onEvent;   /// Event callback function    
Type _type;    /// Event listener type    
ListenerID _listenerID;   /// Event listener ID    
bool _isRegistered;  /// Whether the listener has been added to dispatcher.    
int   _fixedPriority;   // The higher the number, the higher the priority, 0 is for scene graph base priority.    
Node* _node;            // scene graph based priority    
bool _paused;           // Whether the listener is paused    
bool _isEnabled;        // Whether the listener is enabled    

上面的源碼都有英文注釋滥壕,我就不多解釋了,我只說一個(gè)最重要的_isRegistered兽泣,它判斷事件有沒有被注冊绎橘,如果沒有被注冊就不會(huì)觸發(fā)。(如何注冊事件撞叨?將事件加入dispatcher)

3. EventDispatch

在講它之前,我們先了解一下它的一個(gè)重要變量浊洞。

std::vector<EventListener*>* _fixedListeners;  
std::vector<EventListener*>* _sceneGraphListeners;   

sceneGraphListeners:一個(gè)事件(比如說觸摸事件)牵敷,需要按照一定的響應(yīng)序列,依次對這些Node進(jìn)行事件響應(yīng)法希,所以該類型的事件都會(huì)綁定一個(gè)與此相關(guān)聯(lián)的node枷餐,并且響應(yīng)順序是與node在scene下的zorder相關(guān)的。該類型下的事件優(yōu)先級統(tǒng)一為0苫亦。(與渲染樹有關(guān))

fixedListeners:優(yōu)先級根據(jù) fixedPriority 的數(shù)值從小往大排序毛肋、

3.1 事件是如何分發(fā)的(非觸摸事件)
  1. 先獲取事件的監(jiān)聽ID
  2. 根據(jù)這個(gè)監(jiān)聽ID對這個(gè)事件的所有監(jiān)聽器進(jìn)行排序
    先分發(fā)事件到 fixedPriority < 0 的監(jiān)聽器中,然后再分發(fā)到 = 0 的監(jiān)聽器(SceneGraphPriority)中屋剑,最后在分發(fā)到 > 0 的監(jiān)聽器中润匙,如果中途出現(xiàn) onEvent 返回為 true 的結(jié)果,則終止分發(fā)
  3. 獲取事件的所有監(jiān)聽器唉匾,根據(jù)排序一個(gè)一個(gè)觸發(fā)回調(diào)函數(shù)
  4. 如果對當(dāng)前監(jiān)聽ID孕讳,新增加或刪除監(jiān)聽器,那么DirtyFlag就會(huì)標(biāo)記為true巍膘,該監(jiān)聽ID就需要重新進(jìn)行排序厂财。如果當(dāng)時(shí)正在分發(fā)事件,會(huì)把當(dāng)前需要添加的監(jiān)聽器添加到待添加向量(_toAddedListeners)中峡懈,在事件分發(fā)完畢之后監(jiān)聽器需要從toAddedListeners中轉(zhuǎn)移到正式向量中璃饱。
void EventDispatcher::dispatchEvent(Event* event)
{
    ...
    // 先通過event獲取到事件的標(biāo)志ListenerID
    auto listenerID = __getListenerID(event);
    // 排序此事件的所有的監(jiān)聽器
    sortEventListeners(listenerID);
    // 分發(fā)事件邏輯的函數(shù)指針
    auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;
    if (event->getType() == Event::Type::MOUSE) {
        // 如果是鼠標(biāo)事件重新賦值分發(fā)事件的函數(shù)指針
        pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
    }
    // 獲取改事件的所有的監(jiān)聽器
    auto iter = _listenerMap.find(listenerID);
    if (iter != _listenerMap.end())
    {
        // 如果有,取出里面監(jiān)聽器的Vector
        auto listeners = iter->second;
        // 找到對應(yīng)的監(jiān)聽器的時(shí)候會(huì)觸發(fā)的回調(diào)函數(shù)
        auto onEvent = [&event](EventListener* listener) -> bool{
            event->setCurrentTarget(listener->getAssociatedNode());
            // 觸發(fā)onEvent回調(diào)
            listener->_onEvent(event);
            return event->isStopped();
        };
        // 調(diào)用函數(shù)指針分發(fā)事件
        (this->*pfnDispatchEventToListeners)(listeners, onEvent);
    }
 
    ...
}
3.2 dispatchTouchEvent(觸摸事件的分發(fā)機(jī)制)
  1. listener根據(jù)Node的globlal ZOrder優(yōu)先級排序后肪康,依次響應(yīng)荚恶。
  2. 當(dāng)進(jìn)入TouchEvent Began后,所有監(jiān)聽事件都會(huì)依次影響Touch Began磷支。然后再依次響應(yīng)Touch Move等裆甩,而不是一個(gè)一個(gè)由Began->Move->End
3.3 cocos監(jiān)聽事件的bug

只要出現(xiàn)了刪除,修改齐唆,添加監(jiān)聽器的時(shí)候嗤栓,監(jiān)聽器列表需要重新排序,都需要設(shè)置相應(yīng)的 DirtyFlag 操作。但是 Cocos-2dx v3.10 里面的 updateListeners 函數(shù)有刪除監(jiān)聽器的操作茉帅,然而并沒有設(shè)置相應(yīng)的 DirtyFlag 操作叨叙。
會(huì)拋出下面的異常

CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");

Gt0Index() 方法其實(shí)就是獲取到當(dāng)前監(jiān)聽器中 fixedPriority == 0 的監(jiān)聽器在監(jiān)聽器向量中的位置,它只有在給 Listener 排序的時(shí)候會(huì)設(shè)置堪澎,但是如果更新了對應(yīng) ListenerID 的向量(EventListenerVector)擂错,但是沒有重新排序,就會(huì)出現(xiàn) _gt0Index 未及時(shí)更新的情況樱蛤,導(dǎo)致拋出這個(gè)異常钮呀。

引用:
Cocos2dx游戲引擎(3.x)----新的事件分發(fā)機(jī)制
cocos2dx之event事件(一)
cocos2dx之event事件(三):事件分發(fā)器EventDispatcher
Cocos2dx-v3.10 事件分發(fā)機(jī)制源碼解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昨凡,隨后出現(xiàn)的幾起案子爽醋,更是在濱河造成了極大的恐慌,老刑警劉巖便脊,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚂四,死亡現(xiàn)場離奇詭異,居然都是意外死亡哪痰,警方通過查閱死者的電腦和手機(jī)遂赠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晌杰,“玉大人跷睦,你說我怎么就攤上這事±哐荩” “怎么了送讲?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惋啃。 經(jīng)常有香客問我哼鬓,道長,這世上最難降的妖魔是什么边灭? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任异希,我火速辦了婚禮,結(jié)果婚禮上绒瘦,老公的妹妹穿的比我還像新娘称簿。我一直安慰自己,他們只是感情好惰帽,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布憨降。 她就那樣靜靜地躺著,像睡著了一般该酗。 火紅的嫁衣襯著肌膚如雪授药。 梳的紋絲不亂的頭發(fā)上士嚎,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音悔叽,去河邊找鬼莱衩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛娇澎,可吹牛的內(nèi)容都是我干的笨蚁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼趟庄,長吁一口氣:“原來是場噩夢啊……” “哼括细!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戚啥,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤奋单,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后虑鼎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辱匿,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡键痛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年炫彩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片絮短。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡江兢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丁频,到底是詐尸還是另有隱情杉允,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布席里,位于F島的核電站叔磷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奖磁。R本人自食惡果不足惜改基,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咖为。 院中可真熱鬧秕狰,春花似錦、人聲如沸躁染。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吞彤。三九已至我衬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背低飒。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工许昨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褥赊。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓糕档,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拌喉。 傳聞我的和親對象是個(gè)殘疾皇子速那,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

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

  • Cocos的事件分發(fā)機(jī)制,怎么說呢尿背,總感覺有些亂端仰,借此整理一下。先看看與事件分發(fā)相關(guān)的類田藐。 事件相關(guān)的類 Even...
    voiddog閱讀 3,555評論 0 7
  • Cocos2dx源碼賞析(3)之事件分發(fā) 這篇荔烧,繼續(xù)從源碼的角度賞析下Cocos2dx引擎的另一模塊事件分發(fā)處理機(jī)...
    AlphaGL閱讀 1,227評論 2 5
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)汽久,斷路器鹤竭,智...
    卡卡羅2017閱讀 134,707評論 18 139
  • 在iOS開發(fā)中經(jīng)常會(huì)涉及到觸摸事件。本想自己總結(jié)一下景醇,但是遇到了這篇文章臀稚,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載三痰。作者:L...
    WQ_UESTC閱讀 6,026評論 4 26
  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的吧寺?困惑于Cell怎么突然不能點(diǎn)擊了?糾結(jié)于如何實(shí)現(xiàn)這個(gè)奇葩響應(yīng)需求散劫?亦或是...
    Lotheve閱讀 57,325評論 51 599