Cocos2D-X 之 Touch事件

游戏跟视频最大的区别就是互动,玩家可以操控游戏中的角色,现在的移动设备几乎人手一台,基本上全部都是基于触屏操作的,今天就来学习一下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上移除、触摸的时候来电话了等。

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

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

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

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

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

下面分析一下触屏事件处理和执行流程:
用户自定义类继承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就都处理不了。




 cocos2d-x学习群:  277525242

 Android 学习群 :321529736



转自:http://blog.csdn.net/ym19860303/article/details/8477567
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: cocos2d-x 4. 学习之路 cocos2d-x是一款开源的跨平台游戏引擎,支持多种平台,包括iOS、Android、Windows、Mac OS X等。cocos2d-x 4.是最新版本,相比之前版本,它有很多新特性和改进,如增强的渲染性能、更好的3D支持、更好的物理引擎等。 如果你想学习cocos2d-x 4.,可以从以下几个方面入手: 1. 学习基础知识:了解cocos2d-x的基本概念、架构和工作原理,掌握cocos2d-x的编程语言和开发环境。 2. 学习API:熟悉cocos2d-x的API,包括场景管理、精灵、动画、音频、物理引擎等。 3. 学习示例代码:通过阅读和分析cocos2d-x的示例代码,了解如何使用cocos2d-x开发游戏。 4. 学习实践:通过实践开发小游戏,掌握cocos2d-x的开发流程和技巧,提高自己的编程能力。 总之,学习cocos2d-x 4.需要一定的时间和耐心,但只要你有兴趣和热情,相信你一定能够掌握它。 ### 回答2: cocos2d-x是一个强大的游戏引擎,可用于开发移动和桌面游戏。随着cocos2d-x更新至4.0版本,它的功能得到了大幅升级。如果你想学习cocos2d-x 4.0,以下是一些重要的步骤和建议。 1. 更改代码结构 cocos2d-x 4.0中启用了新的代码结构,旨在更好地实现模块化和解耦。新代码结构包括Core、Renderer、2d等模块,使代码更易于维护和升级。要理解新代码结构,请先阅读cocos2d-x官方文档,并针对各个模块学习和熟悉其API。 2. 学习新功能 cocos2d-x 4.0中引入了许多新功能,例如Shader、Render Queue等。学习新功能是非常必要的,因为它们将改变以前的游戏开发模式。了解这些新功能的实现原理,并在自己的项目中应用它们,有助于提高游戏性能和质量。 3. 学习C++11 cocos2d-x 4.0开始支持C++11标准,这意味着你可以使用C++11的新特性来编写更好的代码。要理解C++11的特性,建议通读一遍C++11的官方标准,并尝试在cocos2d-x项目中使用这些新特性。 4. 实战练习 最后,实战练习是学习任何技能的关键。为了更好地学习cocos2d-x 4.0,建议你尝试开发自己的游戏项目。通过尝试解决实际问题,你能更好地理解cocos2d-x的API,并在实践中掌握游戏开发的技术。 总而言之,学习cocos2d-x 4.0需要掌握新的代码结构、新的功能和C++11标准,并通过实际项目实战练习来加深理解。这需要一定的时间和耐心,但只要你认真学习、实践和不断尝试,必定能够取得成功。 ### 回答3: cocos2d-x 4.0是目前市面上非常流行的开源游戏开发引擎,在游戏开发领域有着较为广泛的应用。然而,学习cocos2d-x 4.0需要付出一定的努力和时间。以下是我对cocos2d-x 4.0学习之路的一些经验和建议。 1. 学习基础知识 在开始学习cocos2d-x 4.0之前,我们需要了解一些基础知识,比如C++语言、OpenGL ES等,这些都是cocos2d-x 4.0的底层实现技术。掌握这些基础知识会让我们从事游戏开发时更加得心应手。 2. 学习文档 学习cocos2d-x 4.0需要阅读官方文档,这些文档详细介绍了引擎的各个方面,而且是学习的最佳资料。文档里包括了引擎的安装、使用、开发以及调试等。建议大家先从官网下载文档,并且仔细阅读。 3. 实践和开发 掌握了基础知识以及学习了文档之后,最好的方式就是通过实践和开发来加深对cocos2d-x 4.0的理解。通过实际开发游戏来体验引擎的使用,这样能够更深刻的理解引擎的机制以及遇到的各种问题该如何解决。同时,通过找到一些相近的问题,并通过查阅文档、代码实现等方式来解决问题,可以增强自己的技术水平。 4. 参与社区 cocos2d-x 4.0的官方论坛以及社区非常活跃,里面的开发者也有着丰富的经验和技术,在学习中可以多向论坛、社区里的大牛请教,获得更多的技术指导,同时也可以参与讨论,提出自己的问题和思考来获得反馈,这样可以更快地提高自己的技术。 总之,学习cocos2d-x 4.0需要耐心和对游戏开发的热情。只有通过不断的学习与实践,我们才能最终掌握这个优秀的游戏开发引擎,从而创建属于自己的游戏作品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值