touch分发器:
在Layer::onEnter中设置setTouchEnabled之前,
需要设置TouchMode,区别是调用ccTouchBegan(CCTouch* touch, CCEvent* event)与ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
或者在Layer中重载registerWithTouchDispatcher自己设置
CCTouchDispatcher
CCTouchDispatcher就相当于观察者模式中的发布者,它本身的名字起的就很直观了,分发器嘛。其中包含有添加和移除观察者(订阅者),然后在方法touchesBegan、touchesMoved、touchesEnded、touchesCancelled中分别调用了方法
void touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex)
在这个方法中通过CCTouchHandler的对象调用继承自触摸协议CCTouchDelegate的触摸对象(观察者)中的相应协议:ccTouchBegan。。。等等。如:
CCTargetedTouchHandler *pHandler = NULL;
CCObject* pObj = NULL;
CCARRAY_FOREACH(m_pTargetedHandlers, pObj)
{
pHandler = (CCTargetedTouchHandler *)(pObj);
if (! pHandler)
{
break;
}
bool bClaimed = false;
if (uIndex == CCTOUCHBEGAN)
{.................
///
void CCTouchDispatcher::touchesMoved(CCSet *touches, CCEvent *pEvent)
{
if (m_bDispatchEvents)
{
this->touches(touches, pEvent, CCTOUCHMOVED);
}
}
每个touchesXXX方法会传进touches方法中一个枚举值,touches方法根据这个枚举值来做相应的逻辑处理。
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;
}
// 如果该触摸层设置了吞噬触摸,在该触摸层处理完触摸事件后,就把捕捉到的触摸点移除出触摸点集合pMutableTouches中
// 下面优先比它低的触摸层则接受不到该触摸点并不会作出形影的触摸处理
if (bClaimed && pHandler->isSwallowsTouches())
{
if (bNeedsMutableSet)
{
pMutableTouches->removeObject(pTouch);
}
break;
}
这套机制的优点和缺点都很明显。有点是比较灵活,使用者可以根据需要设置触摸层的触摸优先级和是否吞噬触摸。缺点是开发者需要自己控制各个响应元素的touch priority,需要做一些代码的处理以及前期的优先级规划。其他大部分的引擎使用的是渲染的顺序来进行触摸事件侧分发,zOrder越大,触摸优先级越高。
而且cocos2d-x的消息分发并不会看touch是否发生在响应元素的响应区域范围内与否,只根据priority顺序来传递。例如button无论多小,只要优先级够高,总能最早响应到touch消息。
每增加一个触摸层到触摸层数组中的时候,都会首先根据该层的触摸优先级来把该触摸层插入到数组中的相应位置,注意这个方法是先根据传进去的触摸层传到CCTargetedTouchHandler的方法handlerWithDelegate中,这个方法返回一个CCTargetedTouchHandler对象,这里用基类指针指向他,然后把这个触摸处理对象pHandler添加到触摸处理对象数组m_pTargetedHandlers中:
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;
}
}
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);
}
然后在touches方法中取出数组中元素的时候,就是直接从触摸层集合(数组)中的第一个触摸层开始分发,意思就是触摸优先级最高的层首先会接收到触摸信息,也就是上面说的不管你这个触摸层大小有多大。
分发器中还有一个方法用来设置触摸层的优先级:setPriority():
void CCTouchDispatcher::setPriority(int nPriority, CCTouchDelegate *pDelegate)
{
CCAssert(pDelegate != NULL, "");
CCTouchHandler *handler = NULL;
handler = this->findHandler(pDelegate);
CCAssert(handler != NULL, "");
if (handler->getPriority() != nPriority)
{
handler->setPriority(nPriority);
this->rearrangeHandlers(m_pTargetedHandlers);
this->rearrangeHandlers(m_pStandardHandlers);
}
}
首先去找这个触摸层:
CCTouchHandler* CCTouchDispatcher::findHandler(CCTouchDelegate *pDelegate)
{
CCObject* pObj = NULL;
CCARRAY_FOREACH(m_pTargetedHandlers, pObj)
{
CCTouchHandler* pHandler = (CCTouchHandler*)pObj;
if (pHandler->getDelegate() == pDelegate)
{
return pHandler;
}
}
CCARRAY_FOREACH(m_pStandardHandlers, pObj)
{
CCTouchHandler* pHandler = (CCTouchHandler*)pObj;
if (pHandler->getDelegate() == pDelegate)
{
return pHandler;
}
}
return NULL;
}
还是一贯的顺序,先从触摸处理对象中获取目标触摸层,开始查找,找到的话,就返回,否则转到标准触摸层中查找,找到则返回,找不到就返回NULL。然后出现一个断言。
找到后,先判断这个层的优先级是否和现在设置的优先级相等,相等的话就不再去把触摸层数组中的所有触摸层重新排序了。否则,重新排序触摸层数组。
最新版的cocos2d-x3.0将会采用绘制顺序来分发触摸事件,即绘制顺序的逆序遍历,也就是从游戏世界中的最上层的物件开始接受事件。
注:setTouchPriority设置对象的响应等级,例如在某个panelA里的一个panelB,panel屏蔽了触摸事件并设置的响应等级为N,如果panelB想响应触摸事件,那么panelB调用setTouchPriority设置一个比panelA等级高的响应即可