Cocos2dx------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就都处理不了。
http://blog.csdn.net/zyxhjy/article/details/8126301
Cocos2dx------touch事件相关推荐
- cocos2d-x touch事件
猴子原创,转载请注明出处. 原文地址:http://blog.csdn.net/yanghuiliu/article/details/6963228 头文件申明: virtualbool ccTouc ...
- 深入cocos2d-x中的touch事件
深入cocos2d-x中的touch事件 在文章cocos2d-x中处理touch事件中简单讨论过怎样处理touch事件, 那么今天来深入了解下cocos2d-x中是怎样分发touch事件的. 我们最 ...
- cocos2dx 3.1从零学习(三)——Touch事件(回调,反向传值)
第三讲 Touch 前面两篇我们学习的内容,足够我们做一款简单的小游戏.也能够说,我们已经入门了,能够蹒跚的走路了. 本篇将解说cocos2dx中非常重要的touch回调机制.你肯定记得第一章做定时器 ...
- Cocos2d-x之Touch事件
Touch事件在移动游戏中是很重要的.它们很容易创建和提供多种功能.当你触摸移动设备的屏幕时,屏幕会知道你触摸了什么地方和触摸了什么.然后,会对你的触摸作为出应答. 触摸事件通常被分配一个优先级,优先 ...
- cocos2d-x触摸事件优先级
CCTouchDispatcher是管理cocos2d-x中全部Touch事件派发的类, CCTouchDispatcher中包括了两个CCTouchHandler的列表, 分别存储Standa ...
- Cocos2d-x Touch事件处理机制(better)
from http://www.2cto.com/kf/201308/236359.html 注:下面这位同学对于cocos2d-x 2.x中的触摸机制分析简直可以说是清晰到家了. 重要补充: 下面这 ...
- ViewGroup的Touch事件分发(源码分析)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...
- View的Touch事件分发(二.源码分析)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...
- View的Touch事件分发(一.初步了解)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 一次完整的Touch事件序列为: ACTION_DOWN -> AC ...
- Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点
转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...
最新文章
- 2021年大数据Hadoop(三):Hadoop国内外应用
- 【OpenCV 4开发详解】图像腐蚀
- cubase怎么添加midi设备_Cubase教程丨20分钟快速上手Cubase(下)
- python项目如何打开_python如何打开_linux如何打开python_python程序打开 - 云+社区 - 腾讯云...
- 数学--数论-- HDU 2601 An easy problem(约束和)
- Gym102059A Coloring Roads
- LeetCode 1943. 描述绘画结果(差分思想)
- html5 lang en 乱码,CSS中的html [lang =“en”]和html:lang(en)有什么区别?
- 详解MySQL事务隔离
- vue项目打包部署到Tomcat上,一刷新就报错404
- centos 6.5 thinkpad trackpoint 中间键 滚动设置
- Visual studio的中英文切换
- stm32f103呼吸灯(PWM脉冲宽度调制)
- 转:只要心底热爱,人生就会朝着光明的方向转变
- matlab画图中颜色渐变
- 华为校招前端开发一面
- 操作系统之信号量机制
- 成本管理相关概念和公式总结307
- 常用开发、办公工具推荐
- 玩游戏不慎被盗vx号的小白在线求助腾讯的大佬们
热门文章
- qemuuser模式运行linux,在Android上使用qemu-user运行可执行文件
- unity3d 求两个点长度_Unity3D实现体积光
- javascript返回页面顶部_RobotFramework: 执行JavaScript语句
- wps怎么做时间线_品牌锦囊 I 我想做企业宣传册,我的公司有很多业务线,怎么设计?...
- python实习目的_python爬虫系列---为什么要学习爬虫
- android 自动打印出来,Android:签名打包后关闭Log打印
- 河南大学计算机期末考试题,河南大学计算机与信息工程学院2008期末C#试题
- 简单又帅气的折纸机器人教程_几张纸做出帅气纸飞机,做法简单飞行速度超快,手工折纸飞机...
- 服务器日志监控系统怎么解决,服务器日志监控系统
- git branch commands