Cocos2d-x之CCTouchDispatcher事件分發
使用過CCLayer的都應該知道,CCLayer的眾多父類中有CCTouchDelegate這麼一個類,他使CCLayer能接收touch事件成為可能。cocos2d-x的touch事件是由CCTouchDispatcher這個touch分發器類來進行派發的,所有需要接收touch事件的物件都必須註冊到CCTouchDispatcher中,而只有繼承於CCTouchDelegate的物件才能被註冊到touch分發器中。 先看看cocos2d-x的touch事件的觸發流程,我們能看得見的遊戲介面我且稱之為檢視(view),touch的產生正是從檢視開始的,在程式的訊息處理中心檢測到touch事件時,將事件整理成CCTouch的集合並傳遞給註冊在檢視內的touch分發器CCTouchDispatcher,這個touch分發器是在導演類設定 openglView的時候註冊的: //1、直接設定touch分發器 void CCDirector::setTouchDispatcher(CCTouchDispatcher* pTouchDispatcher) { //設定touch分發器 if (m_pTouchDispatcher != pTouchDispatcher) { CC_SAFE_RETAIN(pTouchDispatcher); CC_SAFE_RELEASE(m_pTouchDispatcher); m_pTouchDispatcher = pTouchDispatcher; } } //2、導演類初始化的時候建立touch分發器 bool CCDirector::init(void) { ...... //導演類初始化的時候生成touch分發器 m_pTouchDispatcher = new CCTouchDispatcher(); m_pTouchDispatcher->init(); ... ... return true; } void CCDirector::setOpenGLView(CCEGLView *pobOpenGLView) { CCAssert(pobOpenGLView, "opengl view should not be null"); if (m_pobOpenGLView != pobOpenGLView) { ...... //註冊touch分發器到openglView中,接收view傳遞過來的touch事件 m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher); m_pTouchDispatcher->setDispatchEvents(true); } } 在檢視touch分發器怎麼處理touch事件之前,先了解傳遞的資料CCTouch,CCtouch的資料很簡單,只有屬性: int m_nId; CCPoint m_point; CCPoint m_prevPoint; CCTouch.cpp: // returns the current touch location in screen coordinates CCPoint CCTouch::getLocationInView() const { //獲取螢幕座標 return m_point; } // returns the current previous location in screen coordinates CCPoint CCTouch::getPreviousLocationInView() const { //獲取上一次的螢幕座標 return m_prevPoint; } // returns the current touch location in OpenGL coordinates CCPoint CCTouch::getLocation() const { //獲取在opengl座標系中的座標 return CCDirector::sharedDirector()->convertToGL(m_point); } // returns the previous touch location in OpenGL coordinates CCPoint CCTouch::getPreviousLocation() const { //獲取上一次在opengl座標系中的位置 return CCDirector::sharedDirector()->convertToGL(m_prevPoint); } // returns the delta position between the current location and the previous location in OpenGL coordinates CCPoint CCTouch::getDelta() const { //返回當前和上次位置在opengl座標系中差值 return ccpSub(getLocation(), getPreviousLocation()); } CCTouch提供查詢座標點的介面。 所有能接收touch事件的物件都需要繼承於CCTouchDelegate,這個類定義了接收touch事件的眾多介面,CCTouchDelegateProtocol.h: class CC_DLL CCTouchDelegate { public: CCTouchDelegate() {} virtual ~CCTouchDelegate() { } virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent); return false;}; // optional virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);} // optional virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} }; //此型別的觸控物件,可以通過ccTouchBegan的返回值決定此次touch是否有後續的反饋 //返回值為false的時候,此次touch後續的moved、ended、cancelled都不再反饋給此觸控物件 //具備touch阻斷功能的觸控物件 class CC_DLL CCTargetedTouchDelegate : public CCTouchDelegate { public: /** Return YES to claim the touch. @since v0 */ virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) { CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);return false;}; // optional virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);} }; //此型別的觸控物件不具有阻止後續反饋的功能,一次touch事件,觸控物件將接收到所有的後續反饋 //典型的觸控物件接收多點觸控資料 class CC_DLL CCStandardTouchDelegate : public CCTouchDelegate { public: // optional virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);} }; 其中定義了兩種觸控物件,一種是接收單觸控並具有阻擋策略,另一種是接收多點觸控 touch分發器中,將觸控物件轉換為觸控物件控制代碼CCTouchHandler之後,才進行儲存和處理的,CCTouchHandler.h: //觸控物件的控制代碼,相當於一個容器,儲存了觸控物件以及觸控物件的觸控優先順序 class CC_DLL CCTouchHandler : public CCObject { public: virtual ~CCTouchHandler(void); /** delegate */ //觸控物件的獲取與設定 CCTouchDelegate* getDelegate(); void setDelegate(CCTouchDelegate *pDelegate); /** 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 */ //建立一個觸控控制代碼,引數為觸控物件和觸控優先順序 static CCTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority); protected: CCTouchDelegate *m_pDelegate; int m_nPriority; int m_nEnabledSelectors; }; /** CCStandardTouchHandler It forwards each event to the delegate. */ //典型觸控控制代碼 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); }; /** CCTargetedTouchHandler Object than contains the claimed touches and if it swallows touches. Used internally by TouchDispatcher */ //具有touch阻斷功能的觸控控制代碼 class CC_DLL CCTargetedTouchHandler : public CCTouchHandler { public: ~CCTargetedTouchHandler(void); /** whether or not the touches are swallowed */ //是否阻止touch事件冒泡,繼續分發給接下來的其他物件 bool isSwallowsTouches(void); void setSwallowsTouches(bool bSwallowsTouches); /** MutableSet that contains the claimed touches */ //獲取集合、此集合中儲存了touch物件,針對這些touch物件,觸控物件將接收他們的後續反饋 CCSet* getClaimedTouches(void); /** 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; CCSet *m_pClaimedTouches; }; 觸控物件控制代碼也有兩種,單點觸控物件控制代碼具有一個集合屬性 CCSet *m_pClaimedTouches,用來儲存得到認可的touch事件,使該事件的後續反饋將對這個觸控物件有效。 接下來看看touch分發器的具體實現,看看他是怎麼樣分發touch訊息的,主要分發邏輯在touches方法中。 CCTouchDispatcher.h: #ifndef __TOUCH_DISPATCHER_CCTOUCH_DISPATCHER_H__ #define __TOUCH_DISPATCHER_CCTOUCH_DISPATCHER_H__ #include "CCTouchDelegateProtocol.h" #include "cocoa/CCObject.h" #include "cocoa/CCArray.h" NS_CC_BEGIN /** * @addtogroup input * @{ */ typedef enum { ccTouchSelectorBeganBit = 1 << 0, ccTouchSelectorMovedBit = 1 << 1, ccTouchSelectorEndedBit = 1 << 2, ccTouchSelectorCancelledBit = 1 << 3, ccTouchSelectorAllBits = ( ccTouchSelectorBeganBit | ccTouchSelectorMovedBit | ccTouchSelectorEndedBit | ccTouchSelectorCancelledBit), } ccTouchSelectorFlag; enum { CCTOUCHBEGAN, CCTOUCHMOVED, CCTOUCHENDED, CCTOUCHCANCELLED, ccTouchMax, }; class CCSet; class CCEvent; struct ccTouchHandlerHelperData { // we only use the type // void (StandardTouchDelegate::*touchesSel)(CCSet*, CCEvent*); // void (TargetedTouchDelegate::*touchSel)(NSTouch*, CCEvent*); int m_type; }; class CC_DLL EGLTouchDelegate { public: virtual void touchesBegan(CCSet* touches, CCEvent* pEvent) = 0; virtual void touchesMoved(CCSet* touches, CCEvent* pEvent) = 0; virtual void touchesEnded(CCSet* touches, CCEvent* pEvent) = 0; virtual void touchesCancelled(CCSet* touches, CCEvent* pEvent) = 0; virtual ~EGLTouchDelegate() {} }; class CCTouchHandler; struct _ccCArray; 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 */ //是否分發touch事件 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. */ //新增接收touch事件的典型觸控物件 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. */ //新增接受touch事件的,具有阻斷功能的觸控物件 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); protected: 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; //是否分發touch事件 bool m_bDispatchEvents; // 4, 1 for each type of event struct ccTouchHandlerHelperData m_sHandlerHelperData[ccTouchMax]; }; // end of input group /// @} NS_CC_END #endif // __TOUCH_DISPATCHER_CCTOUCH_DISPATCHER_H__