首页 > 代码库 > Cocos2dx之touch事件
Cocos2dx之touch事件
今天看了下ccocos2dx touch事件部分的源码,从CCTouch、CCTouchHandler和CCTouchDispatcher简单的做了分析和总结,先直接看源码吧!
1、CCTouch
class CC_DLL CCTouch : public CCObject{public: CCTouch() : m_nId(0), m_startPointCaptured(false) {} /** returns the current touch location in OpenGL coordinates */ CCPoint getLocation() const;//获取当前touch位置,该位置基于OpenGL坐标 /** returns the previous touch location in OpenGL coordinates */ CCPoint getPreviousLocation() const;//获取前一次touch位置,该位置基于OpenGL坐标 /** returns the start touch location in OpenGL coordinates */ CCPoint getStartLocation() const;//获取该touch的起始位置,该位置基于OpenGL坐标 /** returns the delta of 2 current touches locations in screen coordinates */ CCPoint getDelta() const; //获取前后两次位置的偏移量,基于OpenGL坐标 /** returns the current touch location in screen coordinates */ CCPoint getLocationInView() const; //当前touch位置,该位置基于屏幕坐标位置 /** returns the previous touch location in screen coordinates */ CCPoint getPreviousLocationInView() const; //获取touch前一次的位置,基于屏幕坐标位置 /** returns the start touch location in screen coordinates */ CCPoint getStartLocationInView() const; //获取touch起始位置,基于屏幕坐标位置 void setTouchInfo(int id, float x, float y) { m_nId = id; m_prevPoint = m_point; m_point.x = x; m_point.y = y; if (!m_startPointCaptured) { m_startPoint = m_point; m_startPointCaptured = true; } } int getID() const { return m_nId; }private: int m_nId; bool m_startPointCaptured; CCPoint m_startPoint; CCPoint m_point; CCPoint m_prevPoint;};
CCTouch中有三个主要成员,m_startPoint、m_point、m_prevPoint,这三个点都是基于屏幕坐标。将这三个点转化为OpenGL坐标可以用CCDirector::sharedDirector()->convertToGL(m_point)函数来转化。
2、CCTouchHandler、CCStandardTouchHandler和CCTargetedTouchHandler
class CC_DLL CCTouchHandler : public CCObject{public: virtual ~CCTouchHandler(void); /** delegate */ CCTouchDelegate* getDelegate(); //获取touch代理 void setDelegate(CCTouchDelegate *pDelegate); //设置touch代理 /** priority */ int getPriority(void); //获取代理优先级 void setPriority(int nPriority); //获取代理优先级 /** enabled selectors */ int getEnabledSelectors(void); // void setEnalbedSelectors(int nValue); /** initializes a TouchHandler with a delegate and a priority */ virtual bool initWithDelegate(CCTouchDelegate *pDelegate, int nPriority);public: /** allocates a TouchHandler with a delegate and a priority *///创建一个CCTouchHandlerstatic CCTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority);protected: CCTouchDelegate *m_pDelegate; //touch代理 int m_nPriority; //优先级 int m_nEnabledSelectors; //该成员没看出来有什么作用};CCTouchHandler主要将touch代理和优先级封装起来,CCTouchHandler还有两个派生对象: CCStandardTouchHandler和CCTargetedTouchHandler。这两个派生类很简单不需多说,简单的贴上代码吧。class CC_DLL CCStandardTouchHandler : public CCTouchHandler{public: /** initializes a TouchHandler with a delegate and a priority */ virtual bool initWithDelegate(CCTouchDelegate *pDelegate, int nPriority);public: /** allocates a TouchHandler with a delegate and a priority */ static CCStandardTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority);};class CC_DLL CCTargetedTouchHandler : public CCTouchHandler{public: ~CCTargetedTouchHandler(void); /** whether or not the touches are swallowed */ bool isSwallowsTouches(void); //是否吞掉CCTouch void setSwallowsTouches(bool bSwallowsTouches); //设置是否吞掉CCTouch /** MutableSet that contains the claimed touches */ CCSet* getClaimedTouches(void); //获取将要处理的CCTouch的集合 /** initializes a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */ bool initWithDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallow);public: /** allocates a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */ static CCTargetedTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallow);protected: bool m_bSwallowsTouches; //处理CCTouch后是否吞掉该CCTouch CCSet *m_pClaimedTouches; //要处理的CCTouch集合};
3、CCTouch事件分发器CCTouchDispatcher
class CC_DLL CCTouchDispatcher : public CCObject, public EGLTouchDelegate{public: ~CCTouchDispatcher(); bool init(void); CCTouchDispatcher() : m_pTargetedHandlers(NULL) , m_pStandardHandlers(NULL) , m_pHandlersToAdd(NULL) , m_pHandlersToRemove(NULL) {}public: /** Whether or not the events are going to be dispatched. Default: true */ bool isDispatchEvents(void); //事件是否要被分发 void setDispatchEvents(bool bDispatchEvents); //设置是否分发事件 /** Adds a standard touch delegate to the dispatcher‘s list. See StandardTouchDelegate description. IMPORTANT: The delegate will be retained. */ void addStandardDelegate(CCTouchDelegate *pDelegate, int nPriority); //向标准代理容器添加代理 /** Adds a targeted touch delegate to the dispatcher‘s list. See TargetedTouchDelegate description. IMPORTANT: The delegate will be retained. */ void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches); //向目标代理容器添加代理 /** Removes a touch delegate. The delegate will be released */ void removeDelegate(CCTouchDelegate *pDelegate);//移除特定代理 /** Removes all touch delegates, releasing all the delegates */ void removeAllDelegates(void);//移除所有代理 /** Changes the priority of a previously added delegate. The lower the number, the higher the priority */ void setPriority(int nPriority, CCTouchDelegate *pDelegate);//设置特定代理的优先级 void touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex); //分发事件逻辑处理,主要看的函数 //以下是对四种事件的处理 virtual void touchesBegan(CCSet* touches, CCEvent* pEvent); virtual void touchesMoved(CCSet* touches, CCEvent* pEvent); virtual void touchesEnded(CCSet* touches, CCEvent* pEvent); virtual void touchesCancelled(CCSet* touches, CCEvent* pEvent);public: CCTouchHandler* findHandler(CCTouchDelegate *pDelegate); //根据代理查找特定CCTouchHandlerprotected: void forceRemoveDelegate(CCTouchDelegate *pDelegate); void forceAddHandler(CCTouchHandler *pHandler, CCArray* pArray); void forceRemoveAllDelegates(void); void rearrangeHandlers(CCArray* pArray); //重新根据优先级对代理排序 CCTouchHandler* findHandler(CCArray* pArray, CCTouchDelegate *pDelegate);protected: CCArray* m_pTargetedHandlers; //目标事件代理容器 CCArray* m_pStandardHandlers; //标准事件代理容器 bool m_bLocked; //是否被锁 bool m_bToAdd; //是否需要添加 bool m_bToRemove; //是否需要删除 CCArray* m_pHandlersToAdd; //要添加的代理容器 struct _ccCArray *m_pHandlersToRemove; //要删除的代理容器 bool m_bToQuit; //是否要退出 bool m_bDispatchEvents; //是否要处理touch事件 // 4, 1 for each type of event struct ccTouchHandlerHelperData m_sHandlerHelperData[ccTouchMax];};
我们主要看一看void touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex); 这个函数看看touch事件分发器是如何实现事件的分发。先贴上该函数源码
void CCTouchDispatcher::touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex){ CCAssert(uIndex >= 0 && uIndex < 4, ""); //检查4种touch事件的类型 CCSet *pMutableTouches; m_bLocked = true; //正在进行事件分发的时候先锁定,避免代理容器内部发生变化 // optimization to prevent a mutable copy when it is not necessary unsigned int uTargetedHandlersCount = m_pTargetedHandlers->count(); //获取目标事件代理个数 unsigned int uStandardHandlersCount = m_pStandardHandlers->count(); //获取标准事件代理个数 bool bNeedsMutableSet = (uTargetedHandlersCount && uStandardHandlersCount); //需不需要拷贝CCTouch容器 pMutableTouches = (bNeedsMutableSet ? pTouches->mutableCopy() : pTouches); //拷贝CCTouch容器用于向标准touch代理分发事件 struct ccTouchHandlerHelperData sHelper = m_sHandlerHelperData[uIndex]; // // process the target handlers 1st // if (uTargetedHandlersCount > 0) { CCTouch *pTouch; CCSetIterator setIter; for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter) //遍历CCTouch集合 { pTouch = (CCTouch *)(*setIter); CCTargetedTouchHandler *pHandler = NULL; CCObject* pObj = NULL; CCARRAY_FOREACH(m_pTargetedHandlers, pObj) //对于每一个CCTouch,遍历每一个目标事件代理处理器 { pHandler = (CCTargetedTouchHandler *)(pObj); if (! pHandler) { break; } bool bClaimed = false; //是否要得到处理 if (uIndex == CCTOUCHBEGAN) { bClaimed=pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);//调用代理的ccTouchBegan函数 if (bClaimed) //如果ccTouchBegan函数返回true,说明事件要被处理 { pHandler->getClaimedTouches()->addObject(pTouch); //将该touch事件加入到该touch事件处理器的待处理事件容器中 } } else if (pHandler->getClaimedTouches()->containsObject(pTouch)) //判断handler内是否有该CCTouch { // moved ended canceled bClaimed = true; //标记要被处理 switch (sHelper.m_type) { case CCTOUCHMOVED: pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent); //注意处理CCTouchMoved 不会移除相应CCTouch break; case CCTOUCHENDED: pHandler->getDelegate()->ccTouchEnded(pTouch, pEvent); pHandler->getClaimedTouches()->removeObject(pTouch); //从代理handler中的要处理的CCTouch容器中移除该CCTouch break; case CCTOUCHCANCELLED: pHandler->getDelegate()->ccTouchCancelled(pTouch, pEvent); pHandler->getClaimedTouches()->removeObject(pTouch); //从代理handler中的要处理的CCTouch容器中移除该CCTouch break; } } if (bClaimed && pHandler->isSwallowsTouches()) //已经被处理并且要吞掉 { if (bNeedsMutableSet) // { pMutableTouches->removeObject(pTouch); //从用于向标准代理分发事件的容器中移除该CCTouch } break; } } } } // // process standard handlers 2nd //处理标准事件的分发,比目标事件简单 if (uStandardHandlersCount > 0 && pMutableTouches->count() > 0) { CCStandardTouchHandler *pHandler = NULL; CCObject* pObj = NULL; CCARRAY_FOREACH(m_pStandardHandlers, pObj) { pHandler = (CCStandardTouchHandler*)(pObj); if (! pHandler) { break; } switch (sHelper.m_type) { case CCTOUCHBEGAN: pHandler->getDelegate()->ccTouchesBegan(pMutableTouches, pEvent); break; case CCTOUCHMOVED: pHandler->getDelegate()->ccTouchesMoved(pMutableTouches, pEvent); break; case CCTOUCHENDED: pHandler->getDelegate()->ccTouchesEnded(pMutableTouches, pEvent); break; case CCTOUCHCANCELLED: pHandler->getDelegate()->ccTouchesCancelled(pMutableTouches, pEvent); break; } } } if (bNeedsMutableSet) { pMutableTouches->release(); //释放掉拷贝过来用于分发标准事件的touch集合 } // // Optimization. To prevent a [handlers copy] which is expensive // the add/removes/quit is done after the iterations // m_bLocked = false; //解除锁定 if (m_bToRemove) //有需要被移除的代理 { m_bToRemove = false; for (unsigned int i = 0; i < m_pHandlersToRemove->num; ++i) { forceRemoveDelegate((CCTouchDelegate*)m_pHandlersToRemove->arr[i]); } ccCArrayRemoveAllValues(m_pHandlersToRemove); } if (m_bToAdd) //有需要被添加的代理 { m_bToAdd = false; CCTouchHandler* pHandler = NULL; CCObject* pObj = NULL; CCARRAY_FOREACH(m_pHandlersToAdd, pObj) { pHandler = (CCTouchHandler*)pObj; if (! pHandler) { break; } if (dynamic_cast<CCTargetedTouchHandler*>(pHandler) != NULL) { forceAddHandler(pHandler, m_pTargetedHandlers); } else { forceAddHandler(pHandler, m_pStandardHandlers); } } m_pHandlersToAdd->removeAllObjects(); } if (m_bToQuit) //需要退出 { m_bToQuit = false; forceRemoveAllDelegates(); //删除所有代理 }}
从代码中我们可以清楚的看到时间分发的逻辑,cocos2dx将代理分为两种类型:标准事件代理和目标事件代理,事件分发的时候分别处理;事件分为四种事件,CCTOUCHBEGAN、CCTOUCHMOVED、CCTOUCHENDED和CCTOUCHCANCELLED;当调用目标代理的ccTouchBegan函数返回为真说明改代理需要处理该事件,并将该事件暂存到CCTargetedTouchHandler得集合中,当调用ccTouchMoved、ccTouchEnded和ccTouchCanceled时若该代理是否要吞掉该事件则删除标准容器中的该事件。标准事件的分发比较简单略过,还有一点就是,cocos2dx在进行事件分发的时候,将两种容器锁定,避免分发事件的时候容器中的代理有变化,事件分发结束后再将该添加的代理添加,该删除的代理删除。
4、CCTouch、CCTouchHandler和CCTouchDispatcher之间的关系如下图所示: