cocos2dx-触摸分发分析
2016-06-22 18:10
423 查看
本文分析的是cocos2dx-2.2.2的触摸分发机制。
cocos利用底层的接口把消息包装发给了CCTouchDispatcher,ios平台就是用的ceglview这种视图,ios下一般应用编程我们都是用的系统提供的view,这些view可以接受触摸消息,显然ceglview也一样可以。同样也有4个触摸函数,began、moved、ended、cancled。cocos就是在里面进行了包装,这是跟系统直接接触的代码,在这里不进行分析了。
查看CCTouchDispatcher的两个添加接受触摸对象的函数原型
void
CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate,
int nPriority, bool bSwallowsTouches)
CCTouchDispatcher::addStandardDelegate(CCTouchDelegate
*pDelegate, int nPriority)
其中addTargetedDelegate是添加单点触摸的对象,addStandardDelegate是多点。
一、
CCTouchDispatcher怎么添加触摸代理的
m_pTargetedHandlers)会被调用,它会根据优先级把处理对象加到优先级数组中去,代码如下:
上面代码遍历了pArray,其中u是pHandler的插入位置,由于按升序排序,所以遍历遇到一个比它大的就结束,此时u的值就是数组中的索引、在数组中插入的位置。
上面代码就是插入到数组中的代码。其中memmove把index开始的数组元素后移到index+1处,这里不会被覆盖,很安全。最后object插到了数组index处。
二、CCTouchDispatcher怎么把触摸消息分发给那些触摸代理的
上面代码是void CCTouchDispatcher::touches(CCSet *pTouches,
CCEvent *pEvent, unsigned
int uIndex)里面的一段代码,描述了单点触摸怎么分发
for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter)表示遍历一组底层传来的包装好的触摸点。
CCARRAY_FOREACH(m_pTargetedHandlers, pObj)表示遍历所有的触摸对象。
当uIndex == CCTOUCHBEGAN时,执行bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch,
pEvent)它就是执行触摸对象ccTouchBegan的began方法,这个返回值可以是true or false。true表示需要这个点pHandler->getClaimedTouches()->addObject(pTouch)会被执行,后面手指移动这个点的时候,就会根据pHandler->getClaimedTouches()->containsObject(pTouch)来查看触摸代理是否需要处理此点,然后根据事件是moved、ended。。。来调用代理相应方法。
我们发现began返回true时,后面那些优先级高的就接收不到began消息了。if (bClaimed && pHandler->isSwallowsTouches())告诉我们 pHandler->isSwallowsTouches()值为true。查看void
CCLayer::registerWithTouchDispatcher()代码你会发现pDispatcher->addTargetedDelegate(this,
m_nTouchPriority, true),这里第三参数就是是否吞并消息的意思,值为true表示吞并。
单点触摸每次都只会传一个点给相应的4个触摸代理函数。底层触摸是采用中断发给ios的,然后ios再发给cocos,每次发送并不是只发一个,这个取决于我们的触摸移动快慢,以及ios的触摸灵敏度(触摸点的记录数)。cocos的游戏循环mainloop并没有每次自己调用touches,而是ios的app的总循环,在每次处理触摸时发触摸再次发给cocos。然后调用了相应的触摸代理方法,对对象数据进行了更改,接着mainloop调用drewscene进行其它调度的处理,接着递归渲染结点,在此之前结点数据已经得到更新了
三、CCLayer怎么成为触摸代理的
cocos-2.2.2只有CCLayer提供了触摸功能,可以注册为触摸代理,其它类通过继承它可以实现触摸。
CCLayer要想实现触摸,就需要setTouchEnabled(true)就可以了,默认是多点触摸,我们看下为什么setTouchEnabled(true)就行了
onEnter只有在running的时候才被调用,这里表明了当我们通过setTouchEnabled(true)没有调用registerWithTouchDispatcher时,当对象的onEnter调用了后,registerWithTouchDispatcher肯定被调用,所以我们可以放心在任何可以的地方调用setTouchEnabled(true)。registerWithTouchDispatcher代码如下
上面else部分是没用脚本的情况,这里只分析这个。我们发现kCCTouchesAllAtOnce表示多点,调用我们之前分析的CCTouchDispatcher::addStandardDelegate方法,单点调用CCTouchDispatcher::addTargetedDelegate方法。这两个方法就把CCLayer注册成了触摸代理。
m_nTouchPriority默认为0,通过setTouchPriority可以修改这个值,值越大,它就在数组的更后面,更迟被处理。
数组一个固定位置上去了,只能这样子改变在优先级数组中位置了。
setTouchMode也一样setTouchEnabled(false)注销代理,再setTouchEnabled(true)。setTouchEnabled根据true/false决定注销还是注册代理,如果注册就
addTargetedDelegate/addStandardDelegate,如果注销就CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this)把代理去掉
四、如何自己实现触摸代理类
1、继承CCLayer、CCLayerColor,然后重写四个代理方法,设置触摸模式,开启触摸。最后就OK了
2、自己实现一个类,调用addTargetedDelegate/addStandardDelegate注册代理,调用CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this)
注销代理,然后重写4个单点与4个多点代理方法
cocos利用底层的接口把消息包装发给了CCTouchDispatcher,ios平台就是用的ceglview这种视图,ios下一般应用编程我们都是用的系统提供的view,这些view可以接受触摸消息,显然ceglview也一样可以。同样也有4个触摸函数,began、moved、ended、cancled。cocos就是在里面进行了包装,这是跟系统直接接触的代码,在这里不进行分析了。
查看CCTouchDispatcher的两个添加接受触摸对象的函数原型
void
CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate,
int nPriority, bool bSwallowsTouches)
CCTouchDispatcher::addStandardDelegate(CCTouchDelegate
*pDelegate, int nPriority)
其中addTargetedDelegate是添加单点触摸的对象,addStandardDelegate是多点。
一、
CCTouchDispatcher怎么添加触摸代理的
void CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches) { CCTouchHandler *pHandler = CCTargetedTouchHandler::handlerWithDelegate(pDelegate, nPriority, bSwallowsTouches); if (! m_bLocked) { forceAddHandler(pHandler, m_pTargetedHandlers);//根据优先级加到正确位置 } else { /* If pHandler is contained in m_pHandlersToRemove, if so remove it from m_pHandlersToRemove and return. * Refer issue #752(cocos2d-x) */ if (ccCArrayContainsValue(m_pHandlersToRemove, pDelegate)) { ccCArrayRemoveValue(m_pHandlersToRemove, pDelegate); return; } m_pHandlersToAdd->addObject(pHandler); m_bToAdd = true; } }上面源码中forceAddHandler(pHandler,
m_pTargetedHandlers)会被调用,它会根据优先级把处理对象加到优先级数组中去,代码如下:
void CCTouchDispatcher::forceAddHandler(CCTouchHandler *pHandler, CCArray *pArray) { unsigned int u = 0; CCObject* pObj = NULL; CCARRAY_FOREACH(pArray, pObj) { CCTouchHandler *h = (CCTouchHandler *)pObj; if (h) { if (h->getPriority() < pHandler->getPriority()) { ++u; } if (h->getDelegate() == pHandler->getDelegate()) { CCAssert(0, ""); return; } } } pArray->insertObject(pHandler, u); }
上面代码遍历了pArray,其中u是pHandler的插入位置,由于按升序排序,所以遍历遇到一个比它大的就结束,此时u的值就是数组中的索引、在数组中插入的位置。
void ccArrayInsertObjectAtIndex(ccArray *arr, CCObject* object, unsigned int index) { CCAssert(index<=arr->num, "Invalid index. Out of bounds"); CCAssert(object != NULL, "Invalid parameter!"); ccArrayEnsureExtraCapacity(arr, 1); unsigned int remaining = arr->num - index; if( remaining > 0) { memmove((void *)&arr->arr[index+1], (void *)&arr->arr[index], sizeof(CCObject*) * remaining ); } object->retain(); arr->arr[index] = object; arr->num++; }
上面代码就是插入到数组中的代码。其中memmove把index开始的数组元素后移到index+1处,这里不会被覆盖,很安全。最后object插到了数组index处。
二、CCTouchDispatcher怎么把触摸消息分发给那些触摸代理的
for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter) { pTouch = (CCTouch *)(*setIter); CCTargetedTouchHandler *pHandler = NULL; CCObject* pObj = NULL; CCARRAY_FOREACH(m_pTargetedHandlers, pObj) { pHandler = (CCTargetedTouchHandler *)(pObj); // 没代理对象了就退出循环 if (! pHandler) { break; } // 是否需要该点 如果是就会处理 touchmoved touchend bool bClaimed = false; if (uIndex == CCTOUCHBEGAN) { bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent); if (bClaimed) { pHandler->getClaimedTouches()->addObject(pTouch); } } else //pTouch 单点模式的唯一指针 if (pHandler->getClaimedTouches()->containsObject(pTouch)) { // moved ended canceled bClaimed = true; switch (sHelper.m_type) { case CCTOUCHMOVED: pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent); break; case CCTOUCHENDED: pHandler->getDelegate()->ccTouchEnded(pTouch, pEvent); pHandler->getClaimedTouches()->removeObject(pTouch); break; case CCTOUCHCANCELLED: pHandler->getDelegate()->ccTouchCancelled(pTouch, pEvent); pHandler->getClaimedTouches()->removeObject(pTouch); break; } } // 如果夺走该触摸点,并且吞并触摸,则退出循环,不遍历其它代理对象 if (bClaimed && pHandler->isSwallowsTouches()) { if (bNeedsMutableSet) { pMutableTouches->removeObject(pTouch); } break; } } }
上面代码是void CCTouchDispatcher::touches(CCSet *pTouches,
CCEvent *pEvent, unsigned
int uIndex)里面的一段代码,描述了单点触摸怎么分发
for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter)表示遍历一组底层传来的包装好的触摸点。
CCARRAY_FOREACH(m_pTargetedHandlers, pObj)表示遍历所有的触摸对象。
当uIndex == CCTOUCHBEGAN时,执行bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch,
pEvent)它就是执行触摸对象ccTouchBegan的began方法,这个返回值可以是true or false。true表示需要这个点pHandler->getClaimedTouches()->addObject(pTouch)会被执行,后面手指移动这个点的时候,就会根据pHandler->getClaimedTouches()->containsObject(pTouch)来查看触摸代理是否需要处理此点,然后根据事件是moved、ended。。。来调用代理相应方法。
我们发现began返回true时,后面那些优先级高的就接收不到began消息了。if (bClaimed && pHandler->isSwallowsTouches())告诉我们 pHandler->isSwallowsTouches()值为true。查看void
CCLayer::registerWithTouchDispatcher()代码你会发现pDispatcher->addTargetedDelegate(this,
m_nTouchPriority, true),这里第三参数就是是否吞并消息的意思,值为true表示吞并。
单点触摸每次都只会传一个点给相应的4个触摸代理函数。底层触摸是采用中断发给ios的,然后ios再发给cocos,每次发送并不是只发一个,这个取决于我们的触摸移动快慢,以及ios的触摸灵敏度(触摸点的记录数)。cocos的游戏循环mainloop并没有每次自己调用touches,而是ios的app的总循环,在每次处理触摸时发触摸再次发给cocos。然后调用了相应的触摸代理方法,对对象数据进行了更改,接着mainloop调用drewscene进行其它调度的处理,接着递归渲染结点,在此之前结点数据已经得到更新了
三、CCLayer怎么成为触摸代理的
cocos-2.2.2只有CCLayer提供了触摸功能,可以注册为触摸代理,其它类通过继承它可以实现触摸。
CCLayer要想实现触摸,就需要setTouchEnabled(true)就可以了,默认是多点触摸,我们看下为什么setTouchEnabled(true)就行了
void CCLayer::setTouchEnabled(bool enabled) { if (m_bTouchEnabled != enabled) { m_bTouchEnabled = enabled; if (m_bRunning) { if (enabled) { this->registerWithTouchDispatcher(); } else { // have problems? CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this); } } } }如果CCLayer的父结点没有在运行m_bRunning=false,那么CCLayer的m_bRunning也为false,CCScene被replace与setScene后m_bRunning变为true,然后后面加入的结点m_bRunning都会被设置为true。所以上面代码当它父结点没running的时候this->registerWithTouchDispatcher()不会被执行。我们再看下面代码:
void CCLayer::onEnter() { CCDirector* pDirector = CCDirector::sharedDirector(); // register 'parent' nodes first // since events are propagated in reverse order if (m_bTouchEnabled) { this->registerWithTouchDispatcher(); } // then iterate over all the children CCNode::onEnter(); // add this layer to concern the Accelerometer Sensor if (m_bAccelerometerEnabled) { pDirector->getAccelerometer()->setDelegate(this); } // add this layer to concern the keypad msg if (m_bKeypadEnabled) { pDirector->getKeypadDispatcher()->addDelegate(this); } }
onEnter只有在running的时候才被调用,这里表明了当我们通过setTouchEnabled(true)没有调用registerWithTouchDispatcher时,当对象的onEnter调用了后,registerWithTouchDispatcher肯定被调用,所以我们可以放心在任何可以的地方调用setTouchEnabled(true)。registerWithTouchDispatcher代码如下
void CCLayer::registerWithTouchDispatcher() { CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher(); // Using LuaBindings if (m_pScriptTouchHandlerEntry) { if (m_pScriptTouchHandlerEntry->isMultiTouches()) { pDispatcher->addStandardDelegate(this, 0); LUALOG("[LUA] Add multi-touches event handler: %d", m_pScriptTouchHandlerEntry->getHandler()); } else { pDispatcher->addTargetedDelegate(this, m_pScriptTouchHandlerEntry->getPriority(), m_pScriptTouchHandlerEntry->getSwallowsTouches()); LUALOG("[LUA] Add touch event handler: %d", m_pScriptTouchHandlerEntry->getHandler()); } } else { if( m_eTouchMode == kCCTouchesAllAtOnce ) { pDispatcher->addStandardDelegate(this, 0); } else { pDispatcher->addTargetedDelegate(this, m_nTouchPriority, true); } } }
上面else部分是没用脚本的情况,这里只分析这个。我们发现kCCTouchesAllAtOnce表示多点,调用我们之前分析的CCTouchDispatcher::addStandardDelegate方法,单点调用CCTouchDispatcher::addTargetedDelegate方法。这两个方法就把CCLayer注册成了触摸代理。
m_nTouchPriority默认为0,通过setTouchPriority可以修改这个值,值越大,它就在数组的更后面,更迟被处理。
void CCLayer::setTouchPriority(int priority) { if (m_nTouchPriority != priority) { m_nTouchPriority = priority; if( m_bTouchEnabled) { setTouchEnabled(false); setTouchEnabled(true); } } }它会setTouchEnabled(false)注销代理,再setTouchEnabled(true)注册代理,为什么这样子呢,要知道每次addTargetedDelegate/addStandardDelegate都添加到
数组一个固定位置上去了,只能这样子改变在优先级数组中位置了。
void CCLayer::setTouchMode(ccTouchesMode mode) { if(m_eTouchMode != mode) { m_eTouchMode = mode; if( m_bTouchEnabled) { setTouchEnabled(false); setTouchEnabled(true); } } }
setTouchMode也一样setTouchEnabled(false)注销代理,再setTouchEnabled(true)。setTouchEnabled根据true/false决定注销还是注册代理,如果注册就
addTargetedDelegate/addStandardDelegate,如果注销就CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this)把代理去掉
四、如何自己实现触摸代理类
1、继承CCLayer、CCLayerColor,然后重写四个代理方法,设置触摸模式,开启触摸。最后就OK了
2、自己实现一个类,调用addTargetedDelegate/addStandardDelegate注册代理,调用CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this)
注销代理,然后重写4个单点与4个多点代理方法
相关文章推荐
- Cocos2d-x3.x安装指南
- Cocos2d-x开发系列 交叉开发模式一 脚本支持
- cocos-js,数据本地存储
- Cocos2d-x利用xxtea进行图片资源加密
- cocos场景管理器-自己用
- cocos 本周学习api
- 【Cocos2d-x 3.0 中文基础教程】精灵帧缓存
- cocos2dx-lua sprite增加touch监听
- Cocos2d-x手游开发将log记录到文件
- cocos 获得2个点形成的线段的角度
- cocos-圆周运动 物理方法
- cocos-内存管理之sprite材质
- cocos-九宫格数据切割
- Cocos2d-x中CCSprite的Create流程--h
- cocos2dx CCUserDefault
- Cocos2d-JS 相关文章
- cocos2dx JNI DETECTED ERROR IN APPLICATION: illegal class name 'XXX'的错误修复
- cocos2dx常见的46中+22中动作详解
- 从cocos2dx源代码看android和iOS跨平台那些事
- 关于 error C2039: “create”: 不是“cocos2d::GLView”的成员的解决方法