当前位置: 首页 > article >正文

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

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

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

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

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

void GameLayer::registerWithTouchDispatcher()
{cocos2d::CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
}
把相应的CCTouchDelegate添加到CCTouchDispatcher的分发列表中。addTargetedDelegate函数会创建CCTouchDelegate对应的CCTouchHandler对象并添加到CCMutableArraym_pTargetedHandlers中,看源码:
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类源码:
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函数:
void CCTouchDispatcher::touchesBegan(CCSet *touches, CCEvent *pEvent)
{if (m_bDispatchEvents){this->touches(touches, pEvent, CCTOUCHBEGAN);}
}
/**其他三个方法类似 **/

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

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

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

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

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


转载请注明来自: Alex Zhou ,本文链接: http://codingnow.cn/cocos2d-x/783.html
http://www.lryc.cn/news/2418674.html

相关文章:

  • Red Hat Enterprise Linux ISO 全镜像各个版本下载,安装教程
  • 国内外优秀程序员的博客全在这了,请查收
  • 超详细很实用入门必看:Java,C与Python三种大热编程语言比较分析与建议
  • 分享4个优秀的博客导航站
  • 理论物理专题讲义(量子力学与统计物理部分)
  • 循环赛日程表(递归实现)
  • 单表最大2000W行数据
  • 全网最全!解决VirtualBox或VMware启动虚拟机时报错问题“不能为虚拟电脑打开一个新任务”和“Error In suplibOslnit”解决方案超全超详细
  • 分享115个ASP留言日记源码,总有一款适合您
  • 《神探狄仁杰》主题曲《长歌一曲》
  • Java的三种移位运算
  • 笔记-信息系统安全管理-信息系统的安全属性
  • Portal实现原理 --转载
  • 输出“A、B...Z、AA、AB...AZ、BA、BB...BZ.......”的结构
  • squirrel sql client linux,SQuirreL SQL Client
  • 手机qq2012(android)1.0,手机qq2012安卓1.0 几个版本改进后的正式版本
  • fbreader android源码分析,开源阅读器FBReader Android版本的编译
  • 获取flash显示区域 的 getBounds 和 getRect
  • 你用 Python 写过哪些有趣的脚本?
  • 博客系统第1关:博客系统之用户注册
  • 一文读懂面试官都在问的Log4J2漏洞
  • 95-260-058-源码-检查点-CheckpointBarrierHandler
  • 一款挂靠QQ的盗号木马清理记
  • VOIP
  • Zephry开发指南——环境变量
  • 【CTF之Crypto】与佛论禅解密~罰亦般諳醯至上闍切羯哆究南缽寫奢婆罰夢梵究怯娑
  • 十大轻量级Linux发行版汇总
  • Exchange 2007邮件服务器的搭建和部署
  • 解决Linux国内yum源不能用的问题
  • 百度识图原理分析 推测其发展方向…