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事件派发机制的文章,却一直没 ...
随机推荐
- ftp上传错误
明明设置好了权限,但是在上传的时候提示如下错误,但在使用的过程当中,发现有的时候是可以上传的,很奇怪的问题. baidu 了一下,发现是下面的这个设置导致的.改过来后,果然正常. 这个设置只是一个字符 ...
- iOS开发关于AppStore程序的上传流程
主要内容: 1.创建唯一标示符App ID(前提是你的程序在真机上测试没有任何问题) 2.申请发布证书 3.申请发布描述文件 4.iTunes Connect创建App并填写信息 5.选择证书编译打包 ...
- activity_main.xml
activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android ...
- Chapter 4.开放-封闭原则
开放-封闭原则:是说软件实体应该可以扩展,但不可修改. 设计人员必须对于他设计的模块应该对哪种变化封闭做出选择,先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化. 面对需求,对程序的改动是通 ...
- 飞翔(LIS变形)
飞翔 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧.于是,他们决定举办一场 ...
- hdu 4885 TIANKENG’s travel(bfs)
题目链接:hdu 4885 TIANKENG's travel 题目大意:给定N,L,表示有N个加油站,每次加满油能够移动距离L,必须走直线,可是能够为斜线.然后给出sx,sy,ex,ey,以及N个加 ...
- Python的strip()与split()
==>the start 说实话刚开始我对strip()和split()这两个还真的不太懂,后来在网上查了资料才明白. 可能别人觉着这俩很好区分,但是我最开始确实是有点分不清的,或者说不太确定这 ...
- Qt核心剖析: moc
前面我们说过,Qt 不是使用的“标准的” C++ 语言,而是对其进行了一定程度的“扩展”.这里我们从Qt新增加的关键字就可以看出来:signals.slots 或者 emit.所以有人会觉得 Qt 的 ...
- 相邻数字的基数等比确定进制问题pojg2972
解决数制转换问题时,如果所给的数值不是用十进制表示的,一般用一个字符型数组来存放,数组的每个元素分别存储它的一位数字.然后按位转换求和,得到十进制表示,再把十进制转成成其他所求的进制表示.转成的结果也 ...
- HDU 4160 Dolls (最小路径覆盖=顶点数-最大匹配数)
Dolls Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...