zoukankan      html  css  js  c++  java
  • 函数事件cocos2dx 2.X 触屏事件

    时间紧张,先记一笔,后续优化与完善。

        游戏跟视频最大的区别就是互动,玩家可以操控游戏中的角色,当初的移动设备几乎人手一台,基本上全部都是基于触屏操纵的,今天就来学习一下cocos2d-x是怎么实现对触屏操纵的处置的。
    1.首先来懂得一下相干的几个类、处置触屏事件时操纵和执行的流程
    CCTouch:它封装了触摸点,可以通过locationInView函数返回一个CCPoint。
    CCTouchDelegate:它是触摸事件委托,就是系统捕捉到触摸事件后交由它或者它的子类处置,所以我们在处置触屏事件时,必须得继承它。它封装了下面这些处置触屏事件的函数:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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);
     
    virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);

        ccTouchesCancelled和ccTouchCancelled函数很罕用,在接到系统中断通知,须要取消触摸事件的时候才会调用此方法。如:应用长时间无响应、当前view从window上移除、触摸的时候来电话了等。

        CCTargetedTouchDelegateCCStandardTouchDelegate是CCTouchDelegate的子类,类结构图如下:
    函数和事件
    CCStandardTouchDelegate用于处置多点触摸;CCTargetedTouchDelegate用于处置单点触摸。

        CCTouchDispatcher:实现触摸事件分发,它封装了下面这两个函数,可以把CCStandardTouchDelegate和CCTargetedTouchDelegate添加到分发列表中:

        

    1
    2
    void addStandardDelegate(CCTouchDelegate *pDelegate, int nPriority);
    void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches);

        CCTouchHandler:封装了CCTouchDelegate和其对应的优先级,优先级越高,分发的时候越轻易获得事件处置权,CCStandardTouchHandlerCCTargetedTouchHandler是它的子类。

        下面分析一下触屏事件处置和执行流程:
    用户自定义类继承CCTouchDelegate,重写触屏事件处置函数和registerWithTouchDispatcher函数,在init或者onEnter函数中调用registerWithTouchDispatcher函数,如:

        

    1
    2
    3
    4
    void GameLayer::registerWithTouchDispatcher()
    {
         cocos2d::CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate( this , 0, true );
    }

        把响应的CCTouchDelegate添加到CCTouchDispatcher的分发列表中。addTargetedDelegate函数会创建CCTouchDelegate对应的CCTouchHandler对象并添加到CCMutableArraym_pTargetedHandlers中,看源码:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    void CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches)
    {  
         CCTouchHandler *pHandler = CCTargetedTouchHandler::handlerWithDelegate(pDelegate, nPriority, bSwallowsTouches);
         if (! m_bLocked)
         {
             forceAddHandler(pHandler, m_pTargetedHandlers);
         }
         else
         {
             /**....*/
         }
    }
     
    void CCTouchDispatcher::forceAddHandler(CCTouchHandler *pHandler, CCMutableArray *pArray)
    {
         unsigned int u = 0;
     
         CCMutableArray::CCMutableArrayIterator iter;
         for (iter = pArray->begin(); iter != pArray->end(); ++iter)
         {
             CCTouchHandler *h = *iter;
              if (h)
              {
                 if (h->getPriority() < pHandler->getPriority())
                 {
                     ++u;
                 }
     
                 if (h->getDelegate() == pHandler->getDelegate())
                 {
                     CCAssert(0, "" );
                     return ;
                 }
              }
         }
     
         pArray->insertObjectAtIndex(pHandler, u);
    }

        注意forceAddHandler函数中,pHandler是被添加的对象:pHandler->getPriority()的值越小u的值就越小,因此插入到目标容器中的位置也就越靠前,说明优先级的值越小优先级反而越高,也就能先响应事件(CCMenu的默认值是-128)。 前面事件分发时就是从m_pTargetedHandlers中取出CCXXXTouchHandler,然后调用handler对应的delegate的:pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);,执行的是CCTouchDispatcher的touches函数,考虑到篇幅问题,就不贴出详细代码了。该函数首先会先处置targeted 再处置standard,所以CCTargetedTouchDelegate比CCStandardTouchDelegate优先级高。那什么时候触发执行touches函数呢?CCTouchDispatcher继承了EGLTouchDelegate类,EGLTouchDelegate类源码:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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() {}
    };

        CCTouchDispatcher中实现了这四个函数,恰是在这四个函数中调用了touches函数:

        

    1
    2
    3
    4
    5
    6
    7
    8
    void CCTouchDispatcher::touchesBegan(CCSet *touches, CCEvent *pEvent)
    {
         if (m_bDispatchEvents)
         {
             this ->touches(touches, pEvent, CCTOUCHBEGAN);
         }
    }
    /**其他三个方法相似 **/

        这几个触屏处置函数是由详细平台底层调用的,在AppDelegate.cpp中有这段代码:

        

    1
    2
    CCDirector *pDirector = CCDirector::sharedDirector();
    pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());

        继承跟进setOpenGLView函数,发现了这段代码:

        

    1
    2
    3
    CCTouchDispatcher *pTouchDispatcher = CCTouchDispatcher::sharedDispatcher();
    m_pobOpenGLView->setTouchDelegate(pTouchDispatcher);
    pTouchDispatcher->setDispatchEvents( true );

        调用了详细平台下的CCEGLView类中的setTouchDelegate函数。由于我是在windows平台下,所以CCEGLView此时对应CCEGLView_win32.h文件的CCEGLView类,对应的setTouchDelegate函数为:

        

    1
    void    setTouchDelegate(EGLTouchDelegate * pDelegate);

        系统最终通过CCEGLView类的WindowProc函数处置鼠标在Windows窗口的DOWN、MOVE、UP事件,通过pDelegate分离调用touchesBegan、touchesMoved、touchesEnded函数。

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    LRESULT CCEGLView::WindowProc( UINT message, WPARAM wParam, LPARAM lParam)
    {
         switch (message)
         {
         case WM_LBUTTONDOWN:
             if (m_pDelegate && m_pTouch && MK_LBUTTON == wParam)
             {
                 POINT pt = {( short )LOWORD(lParam), ( short )HIWORD(lParam)};
                 if (PtInRect(&m_rcViewPort, pt))
                 {
                     m_bCaptured = true ;
                     SetCapture(m_hWnd);
                     m_pTouch->SetTouchInfo(0, ( float )(pt.x - m_rcViewPort.left) / m_fScreenScaleFactor,
                         ( float )(pt.y - m_rcViewPort.top) / m_fScreenScaleFactor);
                     m_pSet->addObject(m_pTouch);
                     m_pDelegate->touchesBegan(m_pSet, NULL);
                 }
             }
             break ;
     
         case WM_MOUSEMOVE:
             if (MK_LBUTTON == wParam && m_bCaptured)
             {
                 m_pTouch->SetTouchInfo(0, ( float )(( short )LOWORD(lParam)- m_rcViewPort.left) / m_fScreenScaleFactor,
                     ( float )(( short )HIWORD(lParam) - m_rcViewPort.top) / m_fScreenScaleFactor);
                 m_pDelegate->touchesMoved(m_pSet, NULL);
             }
             break ;
     
         case WM_LBUTTONUP:
             if (m_bCaptured)
             {
                 m_pTouch->SetTouchInfo(0, ( float )(( short )LOWORD(lParam)- m_rcViewPort.left) / m_fScreenScaleFactor,
                     ( float )(( short )HIWORD(lParam) - m_rcViewPort.top) / m_fScreenScaleFactor);
                 m_pDelegate->touchesEnded(m_pSet, NULL);
                 m_pSet->removeObject(m_pTouch);
                 ReleaseCapture();
                 m_bCaptured = false ;
             }
             break ;
    /** .... */
    }
    }

        ok,当初应该明白了触屏操纵相干函数的执行进程了,在其他平台下应该相似。

        2. 实现触屏事件处置
    知道了原理之后,实现起来就很简单了:定义一个CCTouchDelegate(或者其子类CCTargetedTouchDelegate/CCStandardTouchDelegate),然后重写那几个处置函数(began、move、end),并把定义好的CCTouchDelegate添加到分发列表中,在onExit函数中实现从分发列表中删除。
    在平常的开辟中,一般有两种方式:(1)继承CCLayer,在层中处置触屏函数。(2)继承CCSprite和CCTouchDelegate(或者其子类)
    上面两种方式,从原理上来说是一样的。
    1. 下面是采用继承CCLayer的方式处置触屏事件。
    (1)CCStandardTouchDelegate
    添加CCStandardTouchDelegate是非常简单的,只须要重写触屏处置函数和调用setIsTouchEnabled(true)。重要代码如下:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //init函数中
    this ->setIsTouchEnabled( true );
     
    void GameLayer::ccTouchesBegan(CCSet* pTouches,CCEvent* pEvent)
    {
         CCSetIterator it = pTouches->begin();
         CCTouch* touch = (CCTouch*)(*it);
         CCpoint touchLocation = touch->locationInView( touch->view() );
         touchLocation = CCDirector::sharedDirector()->convertToGL(m_tBeginPos);
             /** .... **/
    }

        这里为什么没有把CCStandardTouchDelegate添加进分发列表和从分发列表删除的操纵呢,因为setIsTouchEnabled函数已帮我们做了,看源码:

        每日一道理
    一个安静的夜晚,我独自一人,有些空虚,有些凄凉。坐在星空下,抬头仰望美丽天空,感觉真实却由虚幻,闪闪烁烁,似乎看来还有些跳动。美的一切总在瞬间,如同“海市蜃楼”般,也只是刹那间的一闪而过,当天空变得明亮,而这星星也早已一同退去……

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    void CCLayer::setIsTouchEnabled( bool enabled)
    {
         if (m_bIsTouchEnabled != enabled)
         {
             m_bIsTouchEnabled = enabled;
             if (m_bIsRunning)
             {
                 if (enabled)
                 {
                     this ->registerWithTouchDispatcher();
                 }
                 else
                 {
                     // have problems?
                     CCTouchDispatcher::sharedDispatcher()->removeDelegate( this );
                 }
             }
         }
    }
     
    void CCLayer::registerWithTouchDispatcher()
    {
         /** .... **/
         CCTouchDispatcher::sharedDispatcher()->addStandardDelegate( this ,0);
    }
     
    void CCLayer::onExit()
    {
         if ( m_bIsTouchEnabled )
         {
             CCTouchDispatcher::sharedDispatcher()->removeDelegate( this );
             unregisterScriptTouchHandler();
         }
     
         CCNode::onExit();
    }

        (2) CCTargetedTouchDelegate
    直接看cocos2d-x中的CCMenu(菜单)类,它是继承CCLayer的。部份源码如下:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    class CC_DLL CCMenu : public CCLayer, public CCRGBAProtocol
         {
             /** .... */
             virtual void registerWithTouchDispatcher();
     
             /**
             @brief For phone event handle functions
             */
             virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
             virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
             virtual void ccTouchCancelled(CCTouch *touch, CCEvent* event);
             virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
     
             /**
             @since v0.99.5
             override onExit
             */
             virtual void onExit();
     
             /** .... */
         };
    }
     
    //Menu - Events,在CCLayer的onEnter中被调用
         void CCMenu::registerWithTouchDispatcher()
         {
             CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate( this , kCCMenuTouchPriority, true );
         }
     
         bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
         {
             /** .... */
             }
     
      void CCMenu::onExit()
         {
              /** .... */
             CCLayer::onExit();
         }

        2.下面实现继承CCSprite的方式
    定义一个Ball类继承CCSprite和CCTargetedTouchDelegate。源码如下:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class Ball : public CCSprite, public CCTargetedTouchDelegate
    {
    public :
         Ball( void );
         virtual ~Ball( void );
     
         virtual void onEnter();
         virtual void onExit();
     
         virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
         virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
         virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
    /** .... */
     
    };
     
    void Ball::onEnter()
    {
         CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate( this , 0, true );
         CCSprite::onEnter();
    }
     
    void Ball::onExit()
    {
         CCTouchDispatcher::sharedDispatcher()->removeDelegate( this );
         CCSprite::onExit();
    }
     
    bool Ball::ccTouchBegan(CCTouch* touch, CCEvent* event)
    {
         CCPoint touchPoint = touch->locationInView( touch->view() );
         touchPoint = CCDirector::sharedDirector()->convertToGL( touchPoint );   
    /** .... */
         return true ;
    }

        注意:virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)的返回值对触屏消息是有影响的。
    如果返回false,表示不处置ccTouchMoved(),ccTouchEnded(),ccTouchCanceld()方法,而交由后面接收触屏消息的对象处置;如果返回true,表示会处置ccTouchMoved(),ccTouchEnded(),ccTouchCanceld()方法。请看CCTouchDispatcher.cpp的touches函数部份源码,它是用来分发事件的:

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    bool bClaimed = false ;
    if (uIndex == CCTOUCHBEGAN)
    {
         bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);
         //返回true
         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 ;
         }
    }

        
    如果返回true,并且addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches),bSwallowsTouches为true,则表示消耗掉此触屏消息,后面须要接收触屏消息的对象就接收不到触屏消息了

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if (bClaimed && pHandler->isSwallowsTouches())
                    {
                        if (bNeedsMutableSet)
                        {
                            pMutableTouches->removeObject(pTouch);
                        }
     
                        break ;
                    }

        把该触摸对象CCTouch从数组pMutableTouches中移除了,并且跳出当前for循环,而CCStandardTouchHandler须要从pMutableTouches取出触摸对象进行处置的,这样后面的CCTargetedTouchHandler和CCStandardTouchHandler就都处置不了

    文章结束给大家分享下程序员的一些笑话语录: 不会,Intel会维持高利润,也会维持竞争局面,国外的竞争不是打死对方的那种。你看日本有尼康,佳能,索尼,都做相机,大家都过得很滋润。别看一堆厂,其实真正控制的是后面的那几个财团——有些竞争对手,后面其实是一家人。

    --------------------------------- 原创文章 By
    函数和事件
    ---------------------------------

  • 相关阅读:
    [DB] 数据库的连接
    JS leetcode 翻转字符串里的单词 题解分析
    JS leetcode 拥有最多糖果的孩子 题解分析,六一快乐。
    JS leetcode 搜索插入位置 题解分析
    JS leetcode 杨辉三角Ⅱ 题解分析
    JS leetcode 寻找数组的中心索引 题解分析
    JS leetcode 移除元素 题解分析
    JS leetcode 最大连续1的个数 题解分析
    JS leetcode 两数之和 II
    JS leetcode 反转字符串 题解分析
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3100750.html
Copyright © 2011-2022 走看看