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列表
Cocos2dx引擎10-事件派发的更多相关文章
- cocos2d-x 3.10 PageView BUG
		cocos2d-x 3.10 PageView 拖动滚动到下一个单元,没事件,3.11有修复. 
- cocos2dx+lua注册事件函数详解
		coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler 注册点击事件 registerScriptHa ... 
- cocos2dx+lua注册事件函数详解  事件
		coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler ... 
- 48、[源码]-Spring容器创建-初始化事件派发器、监听器等
		48.[源码]-Spring容器创建-初始化事件派发器.监听器等 8.initApplicationEventMulticaster();初始化事件派发器: 获取BeanFactory 从BeanFa ... 
- Java多线程开发系列之番外篇:事件派发线程---EventDispatchThread
		事件派发线程是java Swing开发中重要的知识点,在安卓app开发中,也是非常重要的一点.今天我们在多线程开发中,穿插进来这个线程.分别从线程的来由.原理和使用方法三个方面来学习事件派发线程. 一 ... 
- wex5 实战 框架拓展之2 事件派发与data刷新
		一 前言 讲完公共data,相信大家对框架级的data组件级绑定有了更新的认识,接下来我们继续深入,以求研究明白wex5的框架能力. 在一个web项目中,其实有一个data, 是基础框架必须的data ... 
- 使用lua实现一个简单的事件派发器
		设计一个简单的事件派发器,个人觉得最重要的一点就是如何保证事件派发过程中,添加或删除同类事件,不影响事件迭代顺序和结果,只要解决这一点,其它都好办. 为了使用pairs遍历函数,重写了pairs(lu ... 
- cocos2d-x lua 触摸事件
		cocos2d-x lua 触摸事件 version: cocos2d-x 3.6 1.监听 function GameLayer:onEnter() local eventDispatcher = ... 
- [置顶] Android源码分析-点击事件派发机制
		转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17339857 概述 一直想写篇关于Android事件派发机制的文章,却一直没 ... 
随机推荐
- BZOJ 1552: [Cerc2007]robotic sort( splay )
			kpm大神说可以用块状链表写...但是我不会...写了个splay.... 先离散化 , 然后splay结点加个min维护最小值 , 就可以了... ( ps BZOJ 3506 题意一样 , 双倍经 ... 
- 【 D3.js 入门系列 — 4 】 如何使用比例尺( scale )
			上一章中使用了一个很重要的概念 — 比例尺( scale ),本节将解说其使用方法. 1. 最大值和最小值 在介绍比例尺( scale )之前,先介绍两个经常和比例尺一起出现的函数,在[第3章]中也出 ... 
- 引用 mkimage使用详解
			引用 鱼 的 mkimage使用详解 uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件. mkimage在制作映象文件的时候,是在原来 ... 
- DataSource绑定DataTable.Select()显示system.data.DataRow问题解决的方法
			有时候我们须要在控件中绑定DataTable中设定条件过滤后的数据,此时,在winForm环境中,一些控件不能正确绑定并显示数据内容.这是由于DataTable.Select()返回的是DataRow ... 
- hdu-4302-Holedox Eating-线段树-单点更新,有策略的单点查询
			一開始实在是不知道怎么做,后来经过指导,猛然发现,仅仅须要记录某个区间内是否有值就可以. flag[i]:代表i区间内,共同拥有的蛋糕数量. 放置蛋糕的时候非常好操作,单点更新. ip:老鼠当前的位置 ... 
- ORM框架Hibernate (四) 一对一单向、双向关联映射
			简介 在上一篇博客说了一下多对一映射,这里再说一下一对一关联映射,这种例子在生活中很常见,比如一个人的信息和他的身份证是一对一.又如一夫一妻制等等. 记得在Java编程思想上第一句话是“一切皆对象”, ... 
- Swift实战-QQ在线音乐(第一版)
			//一.*项目准备 1.QQ音乐App 界面素材:(我使用PP助手,将QQ音乐App备份,解压ipa文件 即可得到里面的图片素材) 2.豆瓣电台接口:"http://douban.fm/j/ ... 
- Arduino 入门程序示例之一个 LED(2015-06-11)
			前言 答应了群主写一些示例程序,一直拖延拖延拖延唉.主要还是害怕在各大高手面前班门弄斧……(这也算是给拖延症找一个美好的理由吧),这几天终于下决心要写出来了,各位高手拍砖敬请轻拍啊. 示例程序 首先是 ... 
- BZOJ 1009: [HNOI2008]GT考试( dp + 矩阵快速幂 + kmp )
			写了一个早上...就因为把长度为m的也算进去了... dp(i, j)表示准考证号前i个字符匹配了不吉利数字前j个的方案数. kmp预处理, 然后对于j进行枚举, 对数字0~9也枚举算出f(i, j) ... 
- CentOS的配置文件
			/etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置. /etc/bashrc:为每一个 ... 
