cocos2d-x Touch
转自:http://codingnow.cn/cocos2d-x/783.html
游戏跟视频最大的区别就是互动,玩家可以操控游戏中的角色,现在的移动设备几乎人手一台,基本上全部都是基于触屏操作的,今天就来学习一下cocos2d-x是怎么实现对触屏操作的处理的。
1.首先来了解一下相关的几个类、处理触屏事件时操作和执行的流程
CCTouch:它封装了触摸点,可以通过locationInView函数返回一个CCPoint。
CCTouchDelegate:它是触摸事件委托,就是系统捕捉到触摸事件后交由它或者它的子类处理,所以我们在处理触屏事件时,必须得继承它。它封装了下面这些处理触屏事件的函数:
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添加到分发列表中:
void addStandardDelegate(CCTouchDelegate *pDelegate, int nPriority);
void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches);
CCTouchHandler:封装了CCTouchDelegate和其对应的优先级,优先级越高,分发的时候越容易获得事件处理权,CCStandardTouchHandler和CCTargetedTouchHandler是它的子类。
下面分析一下触屏事件处理和执行流程:
用户自定义类继承CCTouchDelegate,重写触屏事件处理函数和registerWithTouchDispatcher函数,在init或者onEnter函数中调用registerWithTouchDispatcher函数,如:
void GameLayer::registerWithTouchDispatcher()
{
cocos2d::CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, , 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 = ; 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(, "");
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) = ;
virtual void touchesMoved(CCSet* touches, CCEvent* pEvent) = ;
virtual void touchesEnded(CCSet* touches, CCEvent* pEvent) = ;
virtual void touchesCancelled(CCSet* touches, CCEvent* pEvent) = ; 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(, (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(, (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(, (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,);
} 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.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。源码如下:
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, , 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函数部分源码,它是用来分发事件的:
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,则表示消耗掉此触屏消息,后面需要接收触屏消息的对象就接收不到触屏消息了。
if (bClaimed && pHandler->isSwallowsTouches())
{
if (bNeedsMutableSet)
{
pMutableTouches->removeObject(pTouch);
}
break;
}
把该触摸对象CCTouch从数组pMutableTouches中移除了,并且跳出当前for循环,而CCStandardTouchHandler需要从pMutableTouches取出触摸对象进行处理的,这样后面的CCTargetedTouchHandler和CCStandardTouchHandler就都处理不了。
cocos2d-x Touch的更多相关文章
- cocos2d JS touch(触摸监听)-快速添加事件监听器到管理器
cc.eventManager.addListener({ event: cc.EventListener.TOUCH_ALL_AT_ONCE, onTouchesMoved: function (t ...
- cocos2d JS touch屏幕点击事件监听 cc.EventListener.TOUCH
var self = this; this.touchListener = cc.EventListener.create({ event: cc.EventListener.TOUCH_ONE_BY ...
- cocos2dx 3.2 Touch Listen和menu回调实现截屏
在Cocos2d-X 3.x里面,已经集成了截屏功能,单独放在utils命名空间里,实现在base/ccUtils.h文件里面.看下函数申明 /** Capture the entire screen ...
- cocos2d触摸事件处理机制(2.x和3.x变化)
2.x的触摸事件的版本号 触摸事件处理有2种子.以下单点触摸的样本.(另一种多点触摸屏). 创建cocos2d 该项目. 1. 重写下面虚函数. bool ccTouchBegan(cocos2d:: ...
- cpp blog上面看到一哥们写的 下拉列表
#ifndef DROPDOWNLIST_H_INCLUDED#define DROPDOWNLIST_H_INCLUDED namespace azl{ #define DROPDOWNLIST_N ...
- Cocos2d-x 让精灵随手指移动起来二(简单实现)
void HelloWorld::ccTouchMoved(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) { CCSize winSize = C ...
- 解决TableView / ScrollView上的Menu问题(1滑出View区域还可点击2导致点击menu后View不能滑动)
解决TableView / ScrollView上的Menu问题 1划出区域还可点击 重写CCMenu的触摸事件函数 TouchBegin/TouchMove/TouchCancle/TouchEnd ...
- cocos2d-x sprite触摸处理
转自:http://www.cnblogs.com/lancidie/archive/2013/04/01/2993890.html 我们常常需要判断用户的点击操作是否落于某个sprite之上,进而让 ...
- cocos2d-x之蒙板,局部高亮可点,CCRenderTexture
转自:http://www.2cto.com/kf/201207/144656.html 蒙板,局部高亮可点的用处大多是在新手引导的时候,引导玩家一步一步的走游戏的操作流程. 之前写了一个cocos2 ...
- cocos2d-x触屏事件(单点触屏)
转自:http://blog.csdn.net/onerain88/article/details/7550009 一般经常用到的触屏的情况有两种:一种是Layer统一接收触屏消息,然后由程序根据需要 ...
随机推荐
- Android开发之全屏显示的两种方法
1.通过修改清单文件中Theme,实现全屏 <application android:name=".MyApplication" android:allowBackup=&q ...
- C++中const小结
1.const修饰普通变量(非指针变量)const修饰变量,一般有两种写法:const TYPE value;TYPE const value;对于一个非指针的类型TYPE,这两种写法在本质上是一样的 ...
- poj2828
很容易想到一种动态的做法:平衡树…… 或者是二分+树状数组 但,前者编程复杂度较大,而且据说会被卡(没试过):后者理论上超时(据说可以擦边过?): 所以要尝试新的算法: 倒着考虑,显然最后一个对象的位 ...
- [2015编程之美] 资格赛C
#1150 : 基站选址 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上. 网格中有A个用户,每个 ...
- I.MX6 Linux kernel LVDS backlight enable
/*************************************************************************** * I.MX6 Linux kernel LV ...
- Java [leetcode 37]Sudoku Solver
题目描述: Write a program to solve a Sudoku puzzle by filling the empty cells. Empty cells are indicated ...
- Android 隐藏RadoiButton左边按钮
声明方式 添加属性 android:button=“@null”? 代码方式 radioBtn.setButtonDrawable(new StateListDrawable());
- 随心所欲的DateTime显示格式
任何项目,难免会碰到DateTime的显示问题,.net框架虽提供丰富多样的显示方法,但我很少使用,因老忘记细节,每次都要纠结到底月份在前还是年份在前:日期分隔符到底是“/”,还是“\”,还是“-”等 ...
- (Android Studio)添加按钮以及权重问题
此文大部分摘自http://hukai.me/android-training-course-in-chinese/basics/firstapp/building-ui.html 注意到光标所在代码 ...
- JMX学习笔记(二)-Notification
Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...