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 *///創建一個CCTouchHandler
static 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);  //根據代理查找特定CCTouchHandler
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;     //是否要退出
    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之間的關系如下圖所示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM