触摸事件分发中几个代码解读:

怎么说呢,感觉cocos2dx中的消息分发机制,相对于android中触摸事件分发机制要简单的多。因为android中要做区域判断,过滤器,以及父子组件分发给谁等等的逻辑..
cocos2dx 中相对就要简单多了。

如果有一个组件如果想要接收触摸事件,会通过一个继承一个CCTouchDelegate接口注册给CCTouchDispatcher
CCTouchDispatcher 中维护了一个CCTouchHandler的队列。CCTouchHandler 是CCTouchDelegate两个派生类的包装类。
在接到触摸事件之后,遍历 所维护的CCTouchHandler 队列,并按触摸事件类型,调用对应的方法,CCTouchDelegate 接到回调后,再来进行逻辑处理

而 CCTouchDispatcher 实现了一个 EGLTouchDelegate接口。CCDirector会把这个接口以CCEGLView::setTouchDelegate(CCTouchDispatcher)方式注册到CCEGLViewProtocol里,而这个类针对支持的平台都有适配,然后平台会把相应的事件分发下来。

先来看CCTouchDelegate注册事件解除注册两个方法(Standard方式和Target方式类似,只说一种)

注册回调接口

void CCTouchDispatcher::addStandardDelegate(CCTouchDelegate *pDelegate, int nPriority)
{
CCTouchHandler *pHandler = CCStandardTouchHandler::handlerWithDelegate(pDelegate, nPriority);
if (! m_bLocked)
{
forceAddHandler(pHandler, m_pStandardHandlers);
}
else
{
if (ccCArrayContainsValue(m_pHandlersToRemove, pDelegate))
{
ccCArrayRemoveValue(m_pHandlersToRemove, pDelegate);
return;
} m_pHandlersToAdd->addObject(pHandler);
m_bToAdd = true;
}
}

移除回调接口

void CCTouchDispatcher::removeDelegate(CCTouchDelegate *pDelegate)
{
...
if (! m_bLocked)
{
forceRemoveDelegate(pDelegate);
}
else
{
CCTouchHandler *pHandler = findHandler(m_pHandlersToAdd, pDelegate);
if (pHandler)
{
m_pHandlersToAdd->removeObject(pHandler);
return;
}
ccCArrayAppendValue(m_pHandlersToRemove, pDelegate);
m_bToRemove = true;
}
}

先说为什么要加入一个m_bLocked, m_pHandlersToRemove,m_pHandlersToAdd 而不是直接放入m_pStandardHandlers中。

触摸消息的分发是在CCTouchDispatcher::touches方法中实现的

touches方法中,消息的分发是通过 CCARRAY_FOREACH(m_pStandardHandlers,pObj)这样的方式来遍历m_pStandardHandlers的,来依次判断是否需要把触摸事件分发到对应的胡回调接口中。

假设没有做阻塞标识符m_blocked,如果在移除接口的方法调用的时候,恰好正在遍历这个队列,这个时候直接从m_pStandardHandlers移除对象,
很可能会破坏队列结构,很可能会导致脚标越界的异常(就是常说的队列安全)。

所以就需要一个缓存队列缓存add和remove操作,在遍历结束后,把缓存队列中的接口移除或者添加进m_pStandardHandlers中。

另外forceAddHandler(),forceRemoveDelegate 这些方法顾名思义,就是什么也不用管了,直接添加或移除。这个两个方法调用之前,都做了 !m_blocked 判断。

touches 方法解读

首先将阻塞标识位m_bLocked设置为true,防止外部直接改变 m_pTargetedHandlers 队列

m_bLocked = true;

之后 判断了这次分发触摸事件是否是两中处理方式都要做。

unsigned int uTargetedHandlersCount = m_pTargetedHandlers->count();
unsigned int uStandardHandlersCount = m_pStandardHandlers->count();
bool bNeedsMutableSet = (uTargetedHandlersCount && uStandardHandlersCount);
pMutableTouches = (bNeedsMutableSet ? pTouches->mutableCopy() : pTouches);//这里为什么要复制一个队列出来,会下边解释

然后进入Target处理方式的逻辑。这个顺序表明Target的处理优先级要比Standard要高

Target处理循环(代码又删减)

for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter)
{
....
CCARRAY_FOREACH(m_pTargetedHandlers, pObj)//遍历m_pTargetedHandlers,
{
pHandler = (CCTargetedTouchHandler *)(pObj);
bool bClaimed = false;
if (uIndex == CCTOUCHBEGAN)
{
bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);// 标记1
if (bClaimed)
{
pHandler->getClaimedTouches()->addObject(pTouch);
}
} else
if (pHandler->getClaimedTouches()->containsObject(pTouch))//标记2
{
// moved ended canceled
bClaimed = true;
.....
pHandler->getClaimedTouches()->removeObject(pTouch);
break;
} if (bClaimed && pHandler->isSwallowsTouches())//标记3
{
if (bNeedsMutableSet)
{
pMutableTouches->removeObject(pTouch);
}
break;//标记4
}
}
}
}

对应标记解释

标记1:

如果继承的是CCTargetedTouchDelegate 接口,began回调的返回值至关重要,只有返回true的时候,会把当前CCTouch放入CCTargetedTouchDelegate对应包装类的触摸事件集合     中。
标记2

当只有在标记1中触摸事件返回值为true 的情况,才会分发后续事件。

估计有人看到这里会疑惑,当CCTouch 是began时候放入包装类中。如果触摸事件已经是变化成了MOVE 或者 END时候已经是第二次调用touchs方法了,为什么却使用pHandler->getClaimedTouches()->containsObject(pTouch)这样的方式判断的?

事实上,经过测试发现,当一个触摸事件生成之后,从began,到end和cancle,都是同一个对象。

另外,经过测试,这些CCTouch对象是通过多个固定对象来缓存的,而不是每次有触摸就创建一个新对象。多次触摸事件测试结果显示,单点触摸情况下大约有四个左右()的缓存对象


标记3

这个地方的代码非常的巧妙..

如果这个CCTouch已经有一个CCTargetedTouchDelegate的对他处理,且这个CCTargetedTouchDelegate的状态又是isSwallowsTouches==true,即注册监听的时候调用方法addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches)时,第三个参数为true,则不再把这个CCTouch分发给这个组件之后的其他组件。当然,优先级比他高的就没办法屏蔽了。

其中这个“不分发给其他组件”有两个逻辑处理
     1、标记4处的break ,就是当这bSwallowsTouches为true情况。会退出这个CCTouch处理逻辑中的CCARRAY_FOREACH(m_pTargetedHandlers, pObj) 循环,保证了这组件之后的CCTargetedTouchDelegate 都不会接受到这个CCTouch事件

2、如果是有CCStandardTouchDelegate要处理(bNeedsMutableSet==true)的情况,则把这个CCTouch从事件集合中移除,来保证Standard方式的组件不会接收到这个CCTouch事件。同样的原因,因为对CCTargetedTouchDelegate的处理是在对CCSet*pTouches遍历中做的,直接从pTouches中移除又会破坏队列结构,所以有了上边说到的pMutableTouches=pTouches->mutableCopy() 的copy操作

再来看这么写的原因

bool bNeedsMutableSet = (uTargetedHandlersCount && uStandardHandlersCount);
pMutableTouches = (bNeedsMutableSet ? pTouches->mutableCopy() : pTouches);

bNeedsMutableSet = uTargetedHandlersCount && uStandardHandlersCount

bNeedsMutableSet = true&&true  就是标记3解释的地方说到的情况是

bNeedsMutableSet = true&&false 的情况,没有Standard 类型处理方式 需要处理,也就没必要屏蔽这个事件,所以没有copy
bNeedsMutableSet = false&&true 这种情况没有Target的处理,则也不会出现Swallows 为true需要屏蔽的情况,也就不会有移除操作,不用copy
bNeedsMutableSet = false&&false 这种情况什么处理也不做,也不需要copy

这就是为什么 bNeedsMutableSet==true 的情况需要copy操作了

standard的处理循环

  CCARRAY_FOREACH(m_pStandardHandlers, pObj)
{
pHandler = (CCStandardTouchHandler*)(pObj);
switch (sHelper.m_type)
{
case CCTOUCHBEGAN:
pHandler->getDelegate()->ccTouchesBegan(pMutableTouches, pEvent);
break;
........
}
}

从代码可看出,Standard 消息处理方式更简单,什么也不管,直接遍历所有注册过的组件,然后事件集合全部发下去,让组件自己处理。

处理完Standard之后,会将阻塞标记m_blocked设置为false,然后组件开始检查add和remove队列。
并且可以发现,Target处理方式并不是不接受多点触摸,只不过是把多点触摸拆开分发下去的。而Standard是把多点触摸的消息一起发现去的。且分发过程中,是没有做区域判断的。并且,Standard处理方式ccTouchesBegan,move,end等方法的返回值是true或false都没影响

//待证明的假设
1、这边就假设一个场景,一个精灵注册的是Target的方式,并且随着触摸位置来更新精灵的位置。
如果只是拿到了CCTouch 就直接更新精灵的位置,可能会导致两个手指一起按住精灵拖动,因为CCSet *pTouches多次分发下去的,可能会导致精灵在两个手指之间跳动。但是因为这个分发过程是在一个循环中直接分发完成,两个指头触摸事件顺序是不变的,界面都没有来的及重绘,而直接到了最后那个触摸事件的位置上。

2、还是1中的设定,如果在注册监听的时候,所有精灵都没有设置m_bSwallowsTouches属性,可能会导致重叠精灵会被一起拖动--已证明

cocos2dx 中触摸事件分发一些解读的更多相关文章

  1. Cocos2d-x中触摸事件

    理解一个触摸事件可以从时间和空间两方面考虑. 1.触摸事件的时间方面 触摸事件的在时间方面,如下图所示,可以有不同的“按下”.“移动”和“抬起”等阶段,表示触摸是否刚刚开始.是否正在移动或处于静止状态 ...

  2. 一个demo让你彻底理解Android中触摸事件的分发

    注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...

  3. cocos2d-x lua 触摸事件

    cocos2d-x lua 触摸事件 version: cocos2d-x 3.6 1.监听 function GameLayer:onEnter() local eventDispatcher = ...

  4. 安卓中的事件分发机制之View控件

    前言:Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent e ...

  5. Android中的事件分发机制

    Android中的事件分发机制 作者:丁明祥 邮箱:2780087178@qq.com 这篇文章这周之内尽量写完 参考资料: Android事件分发机制完全解析,带你从源码的角度彻底理解(上) And ...

  6. 图解Android触摸事件分发

    Android中触摸事件传递过程中最重要的是dispatchTouchEvent().onInterceptTouchEvent()和onTouchEvent()方法. View和Activity有d ...

  7. iOS中—触摸事件详解及使用

    iOS中--触摸事件详解及使用 (一)初识 要想学好触摸事件,这第一部分的基础理论是必须要学会的,希望大家可以耐心看完. 1.基本概念: 触摸事件 是iOS事件中的一种事件类型,在iOS中按照事件划分 ...

  8. 【Unity游戏开发】用C#和Lua实现Unity中的事件分发机制EventDispatcher

    一.简介 最近马三换了一家大公司工作,公司制度规范了一些,因此平时的业余时间多了不少.但是人却懒了下来,最近这一个月都没怎么研究新技术,博客写得也是拖拖拉拉,周六周天就躺尸在家看帖子.看小说,要么就是 ...

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

    介绍 Cocos2d-X 3.X 引入了一种新的响应用户事件的机制. 涉及三个基本的方面: Event listeners 封装你的事件处理代码 Event dispatcher 向 listener ...

随机推荐

  1. Pandas基础学习与Spark Python初探

    摘要:pandas是一个强大的Python数据分析工具包,pandas的两个主要数据结构Series(一维)和DataFrame(二维)处理了金融,统计,社会中的绝大多数典型用例科学,以及许多工程领域 ...

  2. Python学习笔记4

    根据文件类型选择文件 文件 s s.split('.')[1] 即为文件后缀名,据此判断 输出执行后结果到指定文件 os.system('E:\\Learning\\python\\test_case ...

  3. 我的前端故事----我为什么用GraphQL

    背景 今年我在做一个有关商户的app,这是一个包含商户从入网到审核.从驳回提交到入网维护的完整的生命周期线下推广人员使用的客户端软件,但故事并没有这么简单... 疑问 随着app的逐渐完善,遇到的问题 ...

  4. for 循环语句

    for循环写在<script></script>里面. for(初始条件:循环条件:状态改变){循环内容} 关键词:break:结束此次循环,continue:跳过此次循环,继 ...

  5. C#多线程的用法7-线程间的协作ManualResetEvent

    ManualResetEvent:手动重置事件,它用于线程间同步时用法非常简单也易于理解. private static void MultiThreadSynergicWithManualReset ...

  6. 如何编写更好的SQL查询:终极指南-第三部分

    本次我们学习<如何编写更好的SQL查询>系列的最后一篇文章. 时间复杂度和大O符号 通过前两篇文章,我们已经对查询计划有了一定了解.接下来,我们还可以借助计算复杂度理论,来进一步深入地挖掘 ...

  7. PS小实验-去除水印

    PS小实验-去除水印 水印是一些品牌商覆盖在图片或视频上的一个商标logo或小文本,比如大家最讨厌的百度logo,作者本人也是比较讨厌水印的,让好端端的一张图片变得美中不足. 个人觉得用photosh ...

  8. 【2017集美大学1412软工实践_助教博客】团队作业7——Alpha冲刺之事后诸葛亮

    题目 团队作业7: http://www.cnblogs.com/happyzm/p/6827853.html 团队成绩 评分项目 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 全组 ...

  9. 201521123093 java 第四周学习总结

    1.平面作业 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 答:1.类与方法的注释 2.super关键字代表的是父类,super.方法表示调用的是父类 2. ...

  10. 《JAVA程序设计》第12周学习总结

    1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. InputStream和OutputStream为所有字节流的父类 通过操作流中的字节可处理各种不同类型的数据 ...