cocos2d-x源码分析::触屏分发器(touch_dispatcher)

本来今天想要写一写cocos2d-x中用的最多了一个类CCLayer,可是要写的时候发现CCLayer继承4个类CCNode, CCTouchDelegate,  CCAccelerometerDelegate和CCKeypadDelegate,说起来可能会比较麻烦,所以现在先说一下其中的一个——CCTouchDelegate


关于触屏的类全部都位于cocos2dx的touch_dispatcher文件夹中


CCTouchDelegate其实是用于触屏反应的一个类,子龙山人把这种模式称为委托者模式,而从cocos2dx的这个头文件的名字可以看出,设计者是把这个类设计成一个协议(protocol),下面是这个类的代码

//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;};
    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);}

     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);}
};

这个类定义了单点触屏(ccTouch)和多点触屏(ccTouches)的回调函数,它们都是虚函数,如果你想要创建一个能够对触屏操作产生反应的对象,就需要继承这个类并且覆写(override)相应的函数


这些触屏回调函数中,began, moved和ended都比较容易理解,分别对应触屏开始,移动和结束,另一个cancelled比较少用,对应的是触屏过程中可能会有来电等事件导致游戏进入后台则会触发触屏取消事件


CCTouchDelegate有两个子类CCTargetedTouchDelegate和CCStandardTouchDelegate,分别对应的单点触屏和多点触屏


然后我们可以看一下触屏时间是怎么由CCTouchDispatcher分发的,这个类比较大我就不贴代码了


CCTouchDispatcher有两个protected的CCArray的成员,m_pTargetedHandlers和m_pStandardHandlers,就是说所有接受触屏事件的对象根据单点多点分别加入到这两个数组之中,由dispatcher统一进行事件的分发和处理,现在我们下来讲述一下handler是一个什么的存在


class CC_DLL  CCTouchHandler : public CCObject
{
public:
    virtual ~CCTouchHandler(void);

    CCTouchDelegate* getDelegate();
    void setDelegate(CCTouchDelegate *pDelegate);

    int getPriority(void);
    void setPriority(int nPriority);

    int getEnabledSelectors(void);
    void setEnalbedSelectors(int nValue);

    virtual bool initWithDelegate(CCTouchDelegate *pDelegate, int nPriority);

public:
    static CCTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority);

protected:
    CCTouchDelegate *m_pDelegate;
    int m_nPriority;
    int m_nEnabledSelectors;
};

其实CCTouchHandler就是CCTouchDelagate的封装,其中增加了优先级(priority),代表处理事件的先后顺序(该变量越小越先被处理),还有一个m_nEnabledSelectors,这个我也不知道有什么作用,欢迎大家来指导一下


class CC_DLL  CCStandardTouchHandler : public CCTouchHandler
{
public:
    virtual bool initWithDelegate(CCTouchDelegate *pDelegate, int nPriority);

public:
    static CCStandardTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority);
};

多点触屏的的handler没有什么特别的事情,但单点触屏就不一样了,我们可以看一下


class CC_DLL  CCTargetedTouchHandler : public CCTouchHandler
{
public:
    ~CCTargetedTouchHandler(void);

    bool isSwallowsTouches(void);
    void setSwallowsTouches(bool bSwallowsTouches);

    CCSet* getClaimedTouches(void);

    bool initWithDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallow);
public:
    static CCTargetedTouchHandler* handlerWithDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallow);

protected:
    bool m_bSwallowsTouches;
    CCSet *m_pClaimedTouches;
};

单点触屏的handler新增了两个属性,一个bool值——是否吞噬,另一个是一个集合——被声明触屏点,所以单点触屏比多点触屏多了两个属性


一个是吞噬触屏,当优先级高并且该bool值为真的时候,就会截取这个触屏反应,优先级低的不会对该触屏进行处理


另一个是触屏的声明,其实大家如果观察认真的话,可以发现触屏协议(CCTouchDelegate)中仅有单点触屏的开始(ccTouchBegan)有返回值,当返回true的时候,该触屏点就会被声明,加入到m_pClaimedTouches集合中,当且仅有被声明的触屏点才会进行后续的moved,ended和cancelled处理


现在我们就可以看一下分发器的具体操作

void CCTouchDispatcher::touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex)
{
    CCAssert(uIndex >= 0 && uIndex < 4, "");

    CCSet *pMutableTouches;
    m_bLocked = true;

     unsigned int uTargetedHandlersCount = m_pTargetedHandlers->count();
     unsigned int uStandardHandlersCount = m_pStandardHandlers->count();
    bool bNeedsMutableSet = (uTargetedHandlersCount && uStandardHandlersCount);

    pMutableTouches = (bNeedsMutableSet ? pTouches->mutableCopy() : pTouches);

    struct ccTouchHandlerHelperData sHelper = m_sHandlerHelperData[uIndex];

    //先处理单点触屏
    if (uTargetedHandlersCount > 0)
    {
        CCTouch *pTouch;
        CCSetIterator setIter;
        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;
                }

                bool bClaimed = false;
                if (uIndex == CCTOUCHBEGAN)
                {
		    //从ccTouchBegan获取是否被声明
                    bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);
					
                    if (bClaimed)
                    {
                        pHandler->getClaimedTouches()->addObject(pTouch);
                    }
                } else
                if (pHandler->getClaimedTouches()->containsObject(pTouch))
                {
                    //处理被声明过后加入claimedTouches集合的触屏点的后续操作
                    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;
                }
            }
        }
    }

    //处理多点触屏
    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();
    }

    //do other things
}

分发器的具体流程其实在整个文章中都有一些穿插介绍,这里只是让大家有一个整体的认识,这里无非是要注意一下无论优先级是什么,单点触屏总比多点触屏先处理,其他的也就没什么好讲的了


欢迎大家评论,互相交流



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值