转自:http://codingnow.cn/cocos2d-x/783.html

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

  1. virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
  2. virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
  3. virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
  4. virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent);
  5.  
  6. virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
  7. virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
  8. virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
  9. virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);

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

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

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

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

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

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

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

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

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

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

注意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. class CC_DLL EGLTouchDelegate
  2. {
  3. public:
  4. virtual void touchesBegan(CCSet* touches, CCEvent* pEvent) = ;
  5. virtual void touchesMoved(CCSet* touches, CCEvent* pEvent) = ;
  6. virtual void touchesEnded(CCSet* touches, CCEvent* pEvent) = ;
  7. virtual void touchesCancelled(CCSet* touches, CCEvent* pEvent) = ;
  8.  
  9. virtual ~EGLTouchDelegate() {}
  10. };

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

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

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

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

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

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

ok,现在应该明白了触屏操作相关函数的执行过程了,在其他平台下应该类似。

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

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

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

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

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

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

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

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

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

  1. bool bClaimed = false;
  2. if (uIndex == CCTOUCHBEGAN)
  3. {
  4. bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);
  5. //返回true
  6. if (bClaimed)
  7. {
  8. pHandler->getClaimedTouches()->addObject(pTouch);
  9. }
  10. } else
  11. if (pHandler->getClaimedTouches()->containsObject(pTouch))
  12. {
  13. // moved ended canceled
  14. bClaimed = true;
  15.  
  16. switch (sHelper.m_type)
  17. {
  18. case CCTOUCHMOVED:
  19. pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent);
  20. break;
  21. case CCTOUCHENDED:
  22. pHandler->getDelegate()->ccTouchEnded(pTouch, pEvent);
  23. pHandler->getClaimedTouches()->removeObject(pTouch);
  24. break;
  25. case CCTOUCHCANCELLED:
  26. pHandler->getDelegate()->ccTouchCancelled(pTouch, pEvent);
  27. pHandler->getClaimedTouches()->removeObject(pTouch);
  28. break;
  29. }
  30. }

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

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

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

cocos2d-x Touch的更多相关文章

  1. cocos2d JS touch(触摸监听)-快速添加事件监听器到管理器

    cc.eventManager.addListener({ event: cc.EventListener.TOUCH_ALL_AT_ONCE, onTouchesMoved: function (t ...

  2. cocos2d JS touch屏幕点击事件监听 cc.EventListener.TOUCH

    var self = this; this.touchListener = cc.EventListener.create({ event: cc.EventListener.TOUCH_ONE_BY ...

  3. cocos2dx 3.2 Touch Listen和menu回调实现截屏

    在Cocos2d-X 3.x里面,已经集成了截屏功能,单独放在utils命名空间里,实现在base/ccUtils.h文件里面.看下函数申明 /** Capture the entire screen ...

  4. cocos2d触摸事件处理机制(2.x和3.x变化)

    2.x的触摸事件的版本号 触摸事件处理有2种子.以下单点触摸的样本.(另一种多点触摸屏). 创建cocos2d 该项目. 1. 重写下面虚函数. bool ccTouchBegan(cocos2d:: ...

  5. cpp blog上面看到一哥们写的 下拉列表

    #ifndef DROPDOWNLIST_H_INCLUDED#define DROPDOWNLIST_H_INCLUDED namespace azl{ #define DROPDOWNLIST_N ...

  6. Cocos2d-x 让精灵随手指移动起来二(简单实现)

    void HelloWorld::ccTouchMoved(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) { CCSize winSize = C ...

  7. 解决TableView / ScrollView上的Menu问题(1滑出View区域还可点击2导致点击menu后View不能滑动)

    解决TableView / ScrollView上的Menu问题 1划出区域还可点击 重写CCMenu的触摸事件函数 TouchBegin/TouchMove/TouchCancle/TouchEnd ...

  8. cocos2d-x sprite触摸处理

    转自:http://www.cnblogs.com/lancidie/archive/2013/04/01/2993890.html 我们常常需要判断用户的点击操作是否落于某个sprite之上,进而让 ...

  9. cocos2d-x之蒙板,局部高亮可点,CCRenderTexture

    转自:http://www.2cto.com/kf/201207/144656.html 蒙板,局部高亮可点的用处大多是在新手引导的时候,引导玩家一步一步的走游戏的操作流程. 之前写了一个cocos2 ...

  10. cocos2d-x触屏事件(单点触屏)

    转自:http://blog.csdn.net/onerain88/article/details/7550009 一般经常用到的触屏的情况有两种:一种是Layer统一接收触屏消息,然后由程序根据需要 ...

随机推荐

  1. 函数重载二义性:error C2668: 'pow' : ambiguous call to overloaded function

    2013-07-08 14:42:45 当使用的函数时重载函数时,若编译器不能判断出是哪个函数,就会出现二义性,并给出报错信息. 问题描述: 在.cpp代码中用到pow函数,如下: long int ...

  2. 怎么知道RTL Schematic中的instance与哪段代码对应呢

    2013-06-23 20:15:47 ISE综合后可以看到RTL Schematic,但我们知道在RTL编码时,要经常问自己一个问题“我写的这段代码会综合成什么样的电路呢”.对于一个简单的设计,比如 ...

  3. 摄像头(2)调用系统拍照activity来录像

    import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager ...

  4. Tomcat详解

    解压缩下载的Tomcat压缩包,呈现的目录结构如下. bin:目录存放一些启动和关闭Tomcat的可执行程序和相关内容.conf:存放关于Tomcat服务器的全局配置.lib:目录存放Tomcat运行 ...

  5. poj2192

    初看这道题,以为是先用SA和SC求LCS,再从SC中把SA剔除,看剩下来的是不是SB(……) 显然不对;因为SC与SA的公共子串不止一种,判断不一定正确. 于是考虑SC中每一个字符,如果能够匹配,那么 ...

  6. Java [Leetcode 40]Combination Sum II

    题目描述: Given a collection of candidate numbers (C) and a target number (T), find all unique combinati ...

  7. 【转】提示框第三方库之MBProgressHUD iOS toast效果 动态提示框效果

    原文网址:http://www.zhimengzhe.com/IOSkaifa/37910.html MBProgressHUD是一个开源项目,实现了很多种样式的提示框,使用上简单.方便,并且可以对显 ...

  8. 《深入Java虚拟机学习笔记》- 第4章 网络移动性

    Java虚拟机学习笔记(四)网络移动性

  9. java web接收POST数据

    新建一个ServerForPOSTMethod的动态网站工程

  10. gSoap的 “error LNK2001: 无法解析的外部符号 _namespaces”解决方法

    gSoap是C/C++开发webService服务第三方的公开类库. 出现上述错误是因为缺少必要的头文件导致的. 在用wsdl2h生成头文件的时候,一并生成了类似 xx.nsmap 的文件,这个文件实 ...