zoukankan      html  css  js  c++  java
  • cocos2d-x触摸分发器原理

    屏幕捕捉到触摸消息的派发流程:

    如果有一个组件如果想要接收触摸事件,会通过继承一个CCTouchDelegate接口注册给CCTouchDispatcher,CCTouchDispatcher 中维护了一个CCTouchHandler的队列。CCTouchHandler 是CCTouchDelegate两个派生类的包装类。在接到触摸事件之后,遍历 所维护的CCTouchHandler 队列,并按触摸事件类型,调用对应的方法(began/move/cancel/end),CCTouchDelegate 接到回调后,再来进行逻辑处理。

    而 CCTouchDispatcher 实现了 EGLTouchDelegate接口。CCDirector会把这个接口以CCEGLView::setTouchDelegate(CCTouchDispatcher)方式注册到CCEGLViewProtocol里,而这个类针对支持的平台都有适配,然后平台会把相应的事件分发下来。

    为了实现触摸事件,CCLayer已经封装好了简单的接口(继承了CCTouchDelegate类)来实现触摸事件的响应。

     

    首先,触摸事件有两种:标准触摸代理和目标触摸代理。那么我们先看看如何开启这两种触摸代理。

    1.标准触摸

    在层初始化时调用setTouchEnable(true)方法即可实现标准触摸,实现处理事件回调函数,处理触摸事件即可。

     // optional
         virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) 
         virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) 
         virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent) 
         virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent) 

     

    第一个参数存放的是触摸点集合,第二个参数为cocos2d-iphone遗留下的,在cocos2d-x中没有意义。那么在这4个触摸事件回调函数中加入事件处理代码即可。例:

    void TouchLayer::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
    {
        if(pTouches->count() == 1)
        {
            CCTouch *touch = dynamic_cast<CCTouch*>(pTouches->anyObject());
    //由于在不同平台下触摸点的坐标系与OpenGL呈现区域的参数可能不尽相同,所以触摸点的位置通常与系统相关,须做如下处理即可获取触摸点在游戏中的位置了 CCPoint position
    = touch->locationInView();//获取游戏画面中的点位置 position = CCDirector::sharedDirector()->convertToGL(position);//把屏幕左边转换为游戏坐标 //此处处理触摸事件 } else { //如果不止一个触摸点,则在此处处理多点触摸事件 } }

    那么来看看setTouchEnable做了什么事,才让触摸回调函数被调用的呢

    void CCLayer::setTouchEnabled(bool enabled)
    {
        if (m_bTouchEnabled != enabled)
        {
            m_bTouchEnabled = enabled;
            if (m_bRunning)
            {
                if (enabled)
                {
                    this->registerWithTouchDispatcher();
                }
                else
                {
                    CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
                }
            }
        }
    }

    再进入registerWithTouchDispatcher方法中看到调用了pDispatcher->addStandardDelegate(this, 0)方法,也就是将该对象注册为标准代理。

    对于标准代理,只要事件分发器接受到用户的触摸事件,就会分发给所有的订阅者;还有当系统存在多个触摸点时,所有的触摸点都会传递给回调函数,然后许多情况下每个触摸点之间是独立的,屏幕上是否存在其他触摸点我们并不关心。

    2.我们在来看看目标代理:

    要为该层使用目标代理,首先调用CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true)开启目标代理(第二个参数为优先级,第三个参数为是否吞噬触摸)。然后重载事件回调函数:

        //注意返回类型,函数名,参数有什么不同
        virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) 
        // optional
        virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) 
        virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) 
        virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) 

    第一个参数为CCTouch*,已不再是集合,而是一个触摸点。ccTouchBegan方法返回的是一个布尔值,表明声明是否要捕捉传入的这个触摸点,返回true表示要捕捉,那么在后面Moved和Ended中处理触摸事件即可。如果注册的时候选择了要吞噬触摸,触摸事件不会再分发下去,则优先级低的对象无法接收到触摸。

    那么实现一个对象的触摸代理就分为如下几步了:

    1)此对象继承CCStandardTouchDelegate / CCTargetedTouchDelegate接口

    2)使用addStandardDelegate / addTargetedDelegate方法把自己注册给触摸时间分发器CCTouchDispatcher 

    3)重载事件处理回调函数。(注意目标代理触摸开始时间Began中必须针对需要接受的事件返回true)

    4)当不再需要接收触摸事件时,使用removeDelegate方法来注销触摸事件的接收。

    3. 触摸分发器原理

     来看看CCDispatcher的主要成员

        //注册标准触摸事件
        void addStandardDelegate(CCTouchDelegate *pDelegate, int nPriority);
        //注册目标触摸事件
        void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches);
        //注销触摸派发
        void removeDelegate(CCTouchDelegate *pDelegate);
        void removeAllDelegates(void);
        //重新设定指定对象的事件优先级
        void setPriority(int nPriority, CCTouchDelegate *pDelegate);
        
        //分发事件
        void touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex);
    
    protected:
        CCArray* m_pTargetedHandlers;  //记录注册了带目标的触摸事件对象
         CCArray* m_pStandardHandlers;  //记录注册了标准触摸事件的对象

    当触摸分发器从系统接受到触摸事件后,还需要逐一分发给触摸处理对象,其中事件分发的相关代码主要集中在touches方法中。

    // dispatch events
    void CCTouchDispatcher::touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex)
    {
        //。。。。。。。。。
       // 1。处理带目标的触摸事件
        if (uTargetedHandlersCount > 0)
        {
            CCTouch *pTouch;
            CCSetIterator setIter;
            //1.1 遍历每一个从系统接收到的触摸事件
            for (setIter = pTouches->begin(); setIter != pTouches->end(); ++setIter)
            {
                pTouch = (CCTouch *)(*setIter);
                CCTargetedTouchHandler *pHandler = NULL;
                CCObject* pObj = NULL;
                //1.2 遍历每一个已注册目标触摸事件的对象,分发事件
                CCARRAY_FOREACH(m_pTargetedHandlers, pObj)
                {
                    pHandler = (CCTargetedTouchHandler *)(pObj);
                    if (! pHandler)
                       break;
                    bool bClaimed = false;
                    //首先处理触摸开始时间
                    if (uIndex == CCTOUCHBEGAN)
                    {
                        bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);
                        if (bClaimed)
                        {
                            pHandler->getClaimedTouches()->addObject(pTouch);
                        }
                    } else
                    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;
                        }
                    }
                    //如果此触摸被捕捉,且注册的是吞噬触摸,则设置“吞噬触摸事件”
                    if (bClaimed && pHandler->isSwallowsTouches())
                    {
                        if (bNeedsMutableSet)
                        {
                            pMutableTouches->removeObject(pTouch);
                        }
    
                        break;
                    }
                }
            }
        }
    
        // 2. 处理标准触摸事件
        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;
                }
            }
        }
    
        //。。。。。。
        //删除 标记为吞噬的触摸事件
       
    }

    在触摸集合中的每一个触摸点,按照优先级询问每一个注册到分发器的对象。

    拥有最低优先级(正无穷)的目标代理的优先级 要比 拥有最高优先级(负无穷)的标准代理的优先级 高。

    对于目标代理,只有在开始事件中返回true,后续事件(移动、结束、取消)才会继续分发给目标对象。

    如果设置了吞噬,被捕捉到的点会被吞噬掉,被吞噬的点将立即移除触摸集合,不在分发给后续目标。

    再来看看是哪里调用的touches方法:

    void CCTouchDispatcher::touchesBegan(CCSet *touches, CCEvent *pEvent)
    {
        if (m_bDispatchEvents)
        {
            this->touches(touches, pEvent, CCTOUCHBEGAN);
        }
    }
    
    void CCTouchDispatcher::touchesMoved(CCSet *touches, CCEvent *pEvent)
    {
        if (m_bDispatchEvents)
        {
            this->touches(touches, pEvent, CCTOUCHMOVED);
        }
    }
    
    void CCTouchDispatcher::touchesEnded(CCSet *touches, CCEvent *pEvent)
    {
        if (m_bDispatchEvents)
        {
            this->touches(touches, pEvent, CCTOUCHENDED);
        }
    }
    
    void CCTouchDispatcher::touchesCancelled(CCSet *touches, CCEvent *pEvent)
    {
        if (m_bDispatchEvents)
        {
            this->touches(touches, pEvent, CCTOUCHCANCELLED);
        }
    }
    

      

    值得注意的是:接收触摸的对象并不一定正显示在屏幕上。触摸分发器和引擎中的绘图是相互独立的,所以在不需要的时候及时从触摸分发器中移除触摸代理。

  • 相关阅读:
    Linux内核RPC请求过程
    二分图
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 文本加密
    Java蓝桥杯 算法提高 九宫格
    Java蓝桥杯 算法提高 九宫格
  • 原文地址:https://www.cnblogs.com/songcf/p/3163403.html
Copyright © 2011-2022 走看看