Cocos2dx引擎10-事件派发

本文介绍Cocos2dx事件(下面简称Event)处理机制中的事件分发模块,在Event发生后,进过一系列处理,最后将会分发Event;

1、dispatchEvent& dispatchTouchEvent方法

voidEventDispatcher::dispatchEvent(Event* event)
{
    if (!_isEnabled)  return;
    updateDirtyFlagForSceneGraph();
    DispatchGuard guard(_inDispatch);
    if (event->getType() ==Event::Type::TOUCH) {
       dispatchTouchEvent(static_cast<EventTouch*>(event));
        return;
    }
    auto listenerID = __getListenerID(event);
    sortEventListeners(listenerID);
    auto iter = _listenerMap.find(listenerID);
    if (iter != _listenerMap.end())    {
        auto listeners = iter->second;
        auto onEvent =[&event](EventListener* listener) -> bool{
           event->setCurrentTarget(listener->getAssociatedNode());
            listener->_onEvent(event);
            return event->isStopped();
        };
        dispatchEventToListeners(listeners,onEvent);
    }
    updateListeners(event);
}

在dispatchEvent方法中:

(1)  判断分发Event机制是否使能

(2)  更新脏数据标志

(3)  分发触摸Event

(4)  分发其他类型Event

voidEventDispatcher::dispatchTouchEvent(EventTouch* event)
{
   sortEventListeners(EventListenerTouchOneByOne::LISTENER_ID);
   sortEventListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
    auto oneByOneListeners =getListeners(EventListenerTouchOneByOne::LISTENER_ID);
    auto allAtOnceListeners =getListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
    if (nullptr == oneByOneListeners &&nullptr == allAtOnceListeners)
        return;
    bool isNeedsMutableSet = (oneByOneListeners&& allAtOnceListeners);
    const std::vector<Touch*>&originalTouches = event->getTouches();
    std::vector<Touch*>mutableTouches(originalTouches.size());
    std::copy(originalTouches.begin(),originalTouches.end(), mutableTouches.begin());
    if (oneByOneListeners)
    {
        auto mutableTouchesIter =mutableTouches.begin();
        auto touchesIter =originalTouches.begin();
        for (; touchesIter !=originalTouches.end(); ++touchesIter) {
            bool isSwallowed = false;
            auto onTouchEvent =[&](EventListener* l) -> bool {                             ....
            };
           dispatchEventToListeners(oneByOneListeners, onTouchEvent);
            if (event->isStopped()){
                return;
            }
            if (!isSwallowed)
                ++mutableTouchesIter;
        }
    }
    if (allAtOnceListeners &&mutableTouches.size() > 0) {
        auto onTouchesEvent =[&](EventListener* l) -> bool{
            ....
        };
       dispatchEventToListeners(allAtOnceListeners, onTouchesEvent);
        if (event->isStopped()){
            return;
        }
    }
    updateListeners(event);
}

在dispatchTouchEvent方法中:

(1) 对单指点击&多指点击EventListener列表进行排序;当然排序算法中首先判断目前EventListener列表是否为脏数据,如果是脏数据,则进行排序;排序的具体细节下面会详细讲述

(2) 获取单指点击&多指点击EventListener列表,并判断EventListener是否为空

(3) 获取Event信息

(4) 若单指点击EventListener列表不为空,则分发单指点击Event

(5) 若多指点击EventListener列表不为空,则分发多指点击Event

(6) 更新EventListener列表状态

2、dispatchTouchEvent 方法中EvnetListener排序sortEventListeners

在dispatchTouchEvent方法中使用了sortEventListeners方法对EventListener列表进行排序,下面将详细讲解该方法;

voidEventDispatcher::sortEventListeners(const EventListener::ListenerID&listenerID) {
    DirtyFlag dirtyFlag = DirtyFlag::NONE;
    auto dirtyIter =_priorityDirtyFlagMap.find(listenerID);
    if (dirtyIter !=_priorityDirtyFlagMap.end()){
        dirtyFlag = dirtyIter->second;
    }
    if (dirtyFlag != DirtyFlag::NONE) {
        dirtyIter->second = DirtyFlag::NONE;
        if ((int)dirtyFlag &(int)DirtyFlag::FIXED_PRIORITY) {
           sortEventListenersOfFixedPriority(listenerID);
        }
        if ((int)dirtyFlag &(int)DirtyFlag::SCENE_GRAPH_PRIORITY) {
            auto rootNode =Director::getInstance()->getRunningScene();
            if (rootNode) {
               sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
            }else{
                dirtyIter->second =DirtyFlag::SCENE_GRAPH_PRIORITY;
            }
        }
    }
}

在sortEventListeners方法中:

(1)  在脏数据列表中查找该listenerID的EventListener是否存在脏数据,若不存在脏数据则不需要排序,退出该方法;若存在脏数据,则进行排序

(2)  针对优先级不等于0的EventListener列表进行排序

(3)  针对优先级等于0的EventListener列表进行排序

下面为优先级不等于0的EventListener列表排序方法:

voidEventDispatcher::sortEventListenersOfFixedPriority(constEventListener::ListenerID& listenerID) {
    auto listeners = getListeners(listenerID);
    if (listeners == nullptr) return;
    auto fixedListeners =listeners->getFixedPriorityListeners();
    if (fixedListeners == nullptr) return;
    std::sort(fixedListeners->begin(),fixedListeners->end(), [](const EventListener* l1, const EventListener* l2){
        return l1->getFixedPriority() <l2->getFixedPriority();
    });
   intindex = 0;
    for (auto& listener : *fixedListeners){
        if (listener->getFixedPriority()>= 0)
            break;
        ++index;
    }
    listeners->setGt0Index(index);
}

在sortEventListenersOfFixedPriority方法中:

(1) 根据ID获取EventListener列表,并判断列表是否为空

(2) 获取EventListener列表中优先级不等于0的EventListener列表_fixedListeners

(3) 使用STL中sort方法对_fixedListeners方法从小到大排序

(4) 统计fixedListeners类表中优先级数值小于0的EventListener的个数

从排序的方法可以得知,高优先级(数值越小优先级越高)EventListener先执行;若优先级相同,先注册的EventListener先执行

下面为优先级等于0的EventListener列表排序方法:

voidEventDispatcher::sortEventListenersOfSceneGraphPriority(constEventListener::ListenerID& listenerID, Node* rootNode) {
    auto listeners = getListeners(listenerID);
    if (listeners == nullptr)  return;
    auto sceneGraphListeners =listeners->getSceneGraphPriorityListeners();
    if (sceneGraphListeners == nullptr)  return;
    _nodePriorityIndex = 0;
    _nodePriorityMap.clear();
    visitTarget(rootNode, true);
    std::sort(sceneGraphListeners->begin(),sceneGraphListeners->end(), [this](const EventListener* l1, constEventListener* l2) {
        return_nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];
    });
}

在sortEventListenersOfSceneGraphPriority方法中:

(1) 根据ID获取EventListener列表,并判断列表是否为空

(2) 获取EventListener列表中优先级等于0的EventListener列表_sceneGraphListeners

(3) 使用_globalZOrder值对该Scene下的Node排序

(4) 根据EventListener对应Node的_globalZOrder值从大到小将_sceneGraphListeners列表排序

3、dispatchTouchEvent 方法中dispatchEventToListeners方法

voidEventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, conststd::function<bool(EventListener*)>& onEvent) {
    bool shouldStopPropagation = false;
    auto fixedPriorityListeners =listeners->getFixedPriorityListeners();
    auto sceneGraphPriorityListeners =listeners->getSceneGraphPriorityListeners();
    ssize_t i = 0;
    if (fixedPriorityListeners) {
        if(!fixedPriorityListeners->empty()){
            for (; i <listeners->getGt0Index(); ++i) {
                auto l =fixedPriorityListeners->at(i);
                if (l->isEnabled()&& !l->isPaused() && l->isRegistered() &&onEvent(l)) {
                    shouldStopPropagation =true;
                    break;
                }
            }
        }
    }
    if (sceneGraphPriorityListeners) {
        if (!shouldStopPropagation) {
            for (auto& l :*sceneGraphPriorityListeners) {
                if (l->isEnabled()&& !l->isPaused() && l->isRegistered() &&onEvent(l)) {
                    shouldStopPropagation =true;
                    break;
                }
            }
        }
    }
    if (fixedPriorityListeners) {
        if (!shouldStopPropagation) {
            ssize_t size =fixedPriorityListeners->size();
            for (; i < size; ++i) {
                auto l =fixedPriorityListeners->at(i);
                if (l->isEnabled()&& !l->isPaused() && l->isRegistered() &&onEvent(l)) {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
}

在dispatchEventToListeners函数中:

(1) 获取_fixedListeners&_sceneGraphListeners列表

(2) 当_fixedListeners不为空时;执行_fixedListeners类表中EventToListener处理

(3) 当_sceneGraphListeners不为空时;执行_sceneGraphListeners类表中EventToListener处理

在_fixedListeners类表中EventToListener处理中,优先级小于0的是不执行的处理方法的;EventToListener的处理方法是通过参数传递过来的匿名函数;该匿名函数的实现下面会继续讲述

4、dispatchTouchEvent 方法中单点匿名方法onTouchEvent

auto onTouchEvent =[&](EventListener* l) -> bool {
    EventListenerTouchOneByOne* listener =static_cast<EventListenerTouchOneByOne*>(l);
    if (!listener->_isRegistered) returnfalse;
    event->setCurrentTarget(listener->_node);
    bool isClaimed = false;
    std::vector<Touch*>::iteratorremovedIter;
    EventTouch::EventCode eventCode =event->getEventCode();
    if (eventCode ==EventTouch::EventCode::BEGAN)  {
        if (listener->onTouchBegan)     {
            isClaimed =listener->onTouchBegan(*touchesIter, event);
            if (isClaimed &&listener->_isRegistered)           {
                listener->_claimedTouches.push_back(*touchesIter);
            }
        }
    }
    else if (listener->_claimedTouches.size()> 0
             && ((removedIter =std::find(listener->_claimedTouches.begin(),listener->_claimedTouches.end(), *touchesIter)) != listener->_claimedTouches.end())){
        isClaimed = true;
        switch (eventCode) {
            case EventTouch::EventCode::MOVED:
                if (listener->onTouchMoved) {
                    listener->onTouchMoved(*touchesIter,event);
                }
                break;
            case EventTouch::EventCode::ENDED:
                if (listener->onTouchEnded) {
                    listener->onTouchEnded(*touchesIter,event);
                }
                if (listener->_isRegistered) {
                    listener->_claimedTouches.erase(removedIter);
                }
                break;
            caseEventTouch::EventCode::CANCELLED:
                if (listener->onTouchCancelled){
                    listener->onTouchCancelled(*touchesIter,event);
                }
                if (listener->_isRegistered) {
                    listener->_claimedTouches.erase(removedIter);
                }
                break;
            default:
                CCASSERT(false, "Theeventcode is invalid.");
                break;
        }
    }
    if (event->isStopped()){
        updateListeners(event);
        return true;
    }
    if (isClaimed &&listener->_isRegistered && listener->_needSwallow) {
        if (isNeedsMutableSet)      {
            mutableTouchesIter =mutableTouches.erase(mutableTouchesIter);
            isSwallowed = true;
        }
        return true;
    }
    return false;
};

在匿名方法onTouchEvent中:

(1) 将传递参数强制转换成EventListenerTouchOneByOne类型,并判断是否为空

(2) 获取触摸(Win32下为鼠标点击\拖动)Event类型

(3) 判断Event类型是BEGAN时

a)  调用EventListener在注册时指定的onTouchBegan,并获取返回值

b)  若返回值是true,将该Event的Touch信息放入_claimedTouches中

(4) 判断Event类型不是BEGAN时

a)  _claimedTouches的内容不为空,在_claimedTouches中有该Event的Touch信息

b)  若Event类型是MOVED,调用EventListener在注册时指定的onTouchMoved

c)  若Event类型是ENDED,调用EventListener在注册时指定的onTouchEnded

d)  若Event类型是CANCELLED,调用EventListener在注册时指定的onTouchCancelled

e)  将该Event的Touch信息从_claimedTouches中移除

(5) 若该Event被停止,更新EventListener列表

(6) 若在onTouchBegan返回值为true,并且_needSwallow被设置为true时,将当前Event从多点触摸Event列表中移除

在该匿名方法中,onTouchBegan的返回值很重要,他关注着后续其他触摸操作(onTouchMoved\onTouchEnded\onTouchCancelled)是否执行,关注则_needSwallow标志是否生效;

5、dispatchTouchEvent 方法中多点的匿名方法onTouchEvent

auto onTouchesEvent= [&](EventListener* l) -> bool{
    EventListenerTouchAllAtOnce* listener =static_cast<EventListenerTouchAllAtOnce*>(l);
    if (!listener->_isRegistered) returnfalse;
    event->setCurrentTarget(listener->_node);
    switch (event->getEventCode())
    {
        case EventTouch::EventCode::BEGAN:
            if (listener->onTouchesBegan) {
                listener->onTouchesBegan(mutableTouches,event);
            }
            break;
        case EventTouch::EventCode::MOVED:
            if (listener->onTouchesMoved) {
                listener->onTouchesMoved(mutableTouches,event);
            }
            break;
        case EventTouch::EventCode::ENDED:
            if (listener->onTouchesEnded) {
                listener->onTouchesEnded(mutableTouches,event);
            }
            break;
        case EventTouch::EventCode::CANCELLED:
            if (listener->onTouchesCancelled){
                listener->onTouchesCancelled(mutableTouches,event);
            }
            break;
        default:
            CCASSERT(false, "The eventcodeis invalid.");
            break;
    }
    if (event->isStopped()){
        updateListeners(event);
        return true;
    }

    return false;
};

在匿名方法onTouchEvent中:

(1)  将传递参数强制转换成EventListenerTouchAllAtOnce类型,并判断是否为空

(2)  获取触摸(Win32下为鼠标点击\拖动)Event类型

(3)  若Event类型为BEGAN时,调用EventListener在注册时指定的onTouchBegan方法

(4)  若Event类型为MOVED时,调用EventListener在注册时指定的onTouchesMoved方法

(5)  若Event类型为ENDED时,调用EventListener在注册时指定的onTouchesEnded方法

(6)  若Event类型为CANCELLED时,调用EventListener在注册时指定的onTouchesCancelled方法

(7)  若该Event被停止,更新EventListener列表

时间: 2024-10-08 21:54:12

Cocos2dx引擎10-事件派发的相关文章

Cocos2d-X 3.x 事件分发机制详解

事件分发机制 新事件分发机制:在2.x 版本事件处理时,将要触发的事件交给代理(delegate)处理,再通过实现代理里面的onTouchBegan等方法接收事件,最后完成事件的响应.而在新的事件分发机制中,只需通过创建一个事件监听器-用来实现各种触发后的逻辑,然后添加到事件分发器_eventDispatcher,所有事件监听器由这个分发器统一管理,即可完成事件响应.请参考更多3.0资料... 事件监听器有以下几种: 触摸事件 (EventListenerTouch) 键盘响应事件 (Event

cocos2dx触屏事件详解

版本:2.x 平台iso 先看mian.m文件 //创建一个iso应用 int retVal = UIApplicationMain(argc, argv, nil, @"AppController"); iOS系统会调用AppController 的 didFinishLaunchingWithOptions函数,里面做了一些创建界面的东西 该函数内部有如下代码: cocos2d::CCApplication::sharedApplication()->run(); 注:*.m

cocos2dx+lua注册事件函数详解

coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler 注册点击事件 registerScriptHandler 注册基本事件 包括 触屏 层的进入 退出 事件 registerScriptKeypadHandler 注册键盘事件 registerScriptAccelerateHandler 注册加速事件 registerScriptTouchHandler 详解(可以设置单点或多点) ? 1 2

Cocos2dx引擎优化(1) ---音效系统

Cocos2dx提供的音频库位于CocosDenshion中,其接口由SimpleAudioEngine定义,提供了基本的背景音乐和音效播放. SimpleAudioEngine的实现是夸平台的, 在windows平台上由mci相关API实现; 在android平台上透过JNI,调用android sdk 中的AudioPlayer实现;而在IOS平台上由Cocoa sdk里的Core-Audio实现.但SimpleAudioEngine并不适用于大部分游戏情境,它在Android上的实现需要直

Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread

事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一.事件派发线程的前世今生 事件(Event)派发(Dispatch)线程(Thread)简写为EDT,也就是各个首字母的简写.在一些书或者博客里边也将其译为事件分发线程.事件调度线程.巴拉巴拉,总之,知道这些名字就行.笔者认为这里翻译成派发更准确点. 熟悉Swing和awt编程的小伙伴对事件派发线程

cocos2dx+lua注册事件函数详解 事件

coocs2dx 版本 3.1.1 registerScriptTouchHandler             注册触屏事件 registerScriptTapHandler                  注册点击事件 registerScriptHandler                         注册基本事件 包括 触屏 层的进入 退出 事件 registerScriptKeypadHandler           注册键盘事件 registerScriptAccelera

Cocos2d-x 3.X 事件分发机制

介绍 Cocos2d-X 3.X 引入了一种新的响应用户事件的机制. 涉及三个基本的方面: Event listeners 封装你的事件处理代码 Event dispatcher 向 listener 分发用户事件 Event 对象 包含关于事件的信息 为了响应事件,首先你要创建一个 EventListener,有五种不同的 EventListener. EventListenerTouch 响应触控事件 EventListenerKeyboard 响应键盘事件 EventListenerAcc

Cocos2d-x学习笔记(十六)--------&gt;Cocos2d-x引擎中的网格地图

网格地图 2D游戏网格地图介绍: 在网格地图游戏中,地图由称为"瓷砖"(tile)的一小组图片相互排列组成.这些图片放在一个整体的网格中.得到一个令人信服的游戏场景.网格地图技术为开发者提供了很多便利,使用很少的网格图片元素,就可以组成非常大的场景地图,而且这些大场景并不会浪费硬件资源.而且根据网格元素的不同,可以得到多种多样的场景地图. 网格地图主要分为两大类: 第一类是90度网格地图(Orthogonal Tilemap),这类地图都是使用正方形或者长方形的瓷砖(Tile)组成的,

cocos2dx 点击事件分析(3)

1.在cocos2dx 点击事件分析(2)中,我们已经从java端分析了,单手触摸和多手触摸屏幕. num --- 1,不论单点触摸还是多点触摸,这个值都是1 ids[] --- 触摸事件的ID void CCEGLViewProtocol::handleTouchesBegin(int num, int ids[], float xs[], float ys[]) { CCSet set; for (int i = 0; i < num; ++i) { int id = ids[i]; flo