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
    函数和事件
    ---------------------------------

  • 相关阅读:
    MySQL存储过程
    [转载]JDBC应该始终以PreparedStatement代替Statement
    Restlet入门例子 RESTful web framwork for java
    Cglib的使用方法(3)Mixin
    HDOJ_1220
    精华:OracleHelper类
    web.config详解
    ADO.NET结构
    字典树
    WCF、Net remoting、Web service概念及区别
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3100750.html
Copyright © 2011-2022 走看看