cocos2d-x学习笔记-触屏事件详解

来源:未知 责任编辑:智问网络 发表时间:2013-11-08 08:44 点击:

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

CCTargetedTouchDelegate和CCStandardTouchDelegate是CCTouchDelegate的子类,类结构图如下:

CCStandardTouchDelegate用于处理多点触摸;CCTargetedTouchDelegate用于处理单点触摸。

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

[cpp] view plaincopy
void addStandardDelegate(CCTouchDelegate *pDelegate, int nPriority); 
void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches); 
CCTouchHandler:封装了CCTouchDelegate和其对应的优先级,优先级越高,分发的时候越容易获得事件处理权,CCStandardTouchHandler和CCTargetedTouchHandler是它的子类。

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

[cpp] 
void GameLayer::registerWithTouchDispatcher() 

    cocos2d::CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true); 

把相应的CCTouchDelegate添加到CCTouchDispatcher的分发列表中。addTargetedDelegate函数会创建CCTouchDelegate对应的CCTouchHandler对象并添加到CCMutableArraym_pTargetedHandlers中,看源码:
[cpp
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); 

事件分发时就是从m_pTargetedHandlers中取出CCXXXTouchHandler,然后调用delegate的:pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);,执行的是CCTouchDispatcher的touches函数,考虑到篇幅问题,就不贴出具体代码了。该函数首先会先处理targeted 再处理standard,所以CCTargetedTouchDelegate比CCStandardTouchDelegate优先级高。那什么时候触发执行touches函数呢?CCTouchDispatcher继承了EGLTouchDelegate类,EGLTouchDelegate类源码:
[cpp] 
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函数:
[cpp] 
void CCTouchDispatcher::touchesBegan(CCSet *touches, CCEvent *pEvent) 

    if (m_bDispatchEvents) 
    { 
        this->touches(touches, pEvent, CCTOUCHBEGAN); 
    } 

/**其他三个方法类似 **/ 

这几个触屏处理函数是由具体平台底层调用的,在AppDelegate.cpp中有这段代码:
[cpp] 
CCDirector *pDirector = CCDirector::sharedDirector(); 
pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView()); 

继续跟进setOpenGLView函数,发现了这段代码:
[cpp] 
CCTouchDispatcher *pTouchDispatcher = CCTouchDispatcher::sharedDispatcher(); 
m_pobOpenGLView->setTouchDelegate(pTouchDispatcher); 
pTouchDispatcher->setDispatchEvents(true); 

调用了具体平台下的CCEGLView类中的setTouchDelegate函数。由于我是在windows平台下,所以CCEGLView此时对应CCEGLView_win32.h文件的CCEGLView类,对应的setTouchDelegate函数为:
[cpp] 
void    setTouchDelegate(EGLTouchDelegate * pDelegate); 

系统最终通过CCEGLView类的WindowProc函数处理鼠标在Windows窗口的DOWN、MOVE、UP事件,通过pDelegate分别调用touchesBegan、touchesMoved、touchesEnded函数。
[cpp] 
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)。主要代码如下:

[cpp]
//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函数已经帮我们做了,看源码:
[cpp]
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的。部分源码如下:
[cpp] 
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。源码如下:
[cpp]
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()方法,并且消耗掉此触屏消息,后面需要接收触屏消息的对象就接收不到触屏消息了。

    发表评论
    请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
    用户名: 验证码:点击我更换图片
    最新评论 更多>>

    推荐热点

    • cocos2d-x学习笔记(19)--label 、label atlas
    • cocos2d-x学习笔记(23)--地图的使用3--CCTMXLayer
    • Cocos2d-x学习(一):HelloWorld
    • cocos2dx在xcode下开发,编译到android上(2)
    • cocos2d 设置屏幕默认方向
    • cocos2d-x学习笔记(22)--地图的使用2(TMX) --Z-Order、AnchorPoi
    • Cocos2d-x 2.0 之 Actions “三板斧” 之一
    • cocos2d-x学习笔记(18)--游戏打包(windows平台)
    • cocos2d-x学习笔记(16)--spritesheet(精灵表单)
    网站首页 - 友情链接 - 网站地图 - TAG标签 - RSS订阅 - 内容搜索
    Copyright © 2008-2015 计算机技术学习交流网. 版权所有

    豫ICP备11007008号-1