Cocos2d-x(1) 触摸输入
触摸输入
1.使用CCLayer 响应触摸事件--此处是标准触摸(standard touch)事件
为了处理屏幕输入事件,最简单的解决方案是利用CCLayer 开启内建的触摸输入支持。在介绍CCLayer 的时候提到过,它的一个十分重要的作用就是接收输入事件,因此层封装了触摸输入的处理接口。一般情况下,我们可以通过TouchEnable属性来开启或关闭接收触摸输入。
在开启了层的触摸输入支持后,我们就可以在层中处理触摸事件了。CCLayer 实现了以下4 个方法,当引擎接收到触摸事件时,这些方法就会被调用:
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);
以上4 个方法都声明为虚函数,这意味着我们通过重载的方式来处理用户输入事件。实际上,这 4 个函数来自于CCStandardTouchDelegate 接口,是触摸事件的回调函数,分别对应触摸的开始、移动、结束和取消操作。传入参数pTouches是一个CCTouch 对象(表示一个触摸点)的集合,其中包含当前触摸事件中的所有触摸点。传入参数 pEvent是由Cocos2d-iPhone 引擎遗留下来的形式参数,在 Cocos2d-x当前版本中没有意义(-_-|||)。
触摸事件分为4 类,其中结束事件和取消事件容易使人困惑。通常,当玩家的触摸动作完成时(例如抬手和手指离开屏幕等),会引起触摸结束事件。而触摸取消的情况较少,仅当触摸过程中程序被调入后台时才会出现。
以移动事件为例,下面是事件处理函数的一种常见实现方法:
void TouchLayer::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) { if(pTouches->count() == 1) { CCTouch* touch = dynamic_cast<CCTouch*>(pTouches->anyObject()); CCPoint position = touch->locationInView(); position = CCDirector::sharedDirector() ->convertToGL(position); //在此处理触摸事件 } else { //如果不止一个触摸点,则在此处理多点触摸事件 } }
在响应函数中,我们从pTouches中取出表示触摸点的CCTouch 对象,并利用触摸点信息完成一系列操作。
在这个例子中,我们首先判断了触摸点的数目,如果只有一个触摸点,则取出该触摸点,然后获取触摸点的位置。由于在不同平台下触摸点的坐标系与OpenGL呈现区域的参数可能不尽相同,所以触摸点的位置通常与系统相关。不过,Cocos2d-x已经为我们处理好了这个问题,只需要仿照上面的代码,调用CCTouch::locationInView 方法获取游戏画面中的点位置,然后再调用CCDirector::convertToGL 方法把屏幕坐标转换为游戏坐标,就可以获取触摸点在游戏中的位置了。
利用层来实现触摸十分简便,然而只要玩家触摸了屏幕,所有响应触摸事件的层都会被触发。当层的数量很多时,维护多个层的触摸事件就成了一件复杂的事情。因此,在实际开发中,我们通常单独建立一个触摸层。用触摸层来接收用户输入事件,并根据需要通知游戏中的其他部件来响应触摸事件。
具体实现连接:http://www.cnblogs.com/TS-qrt/articles/CCLayer_click.html!!(也可参考后文,最后的4个步骤)
2.两种Cocos2d-x 触摸事件
我们已经见到了CCLayer 处理触摸事件的方法。当我们开启 CCLayer 的TouchEnable 属性后,层中的 4 个回调函数就会在接收到事件时被触发,我们把这类事件称作标准触摸(standard touch)事件,它的特点是任何层都会平等地接收全部触摸事件。
除此以外,Cocos2d-x还提供了另一种被称作带目标的触摸事件(targeted touch)机制。在带目标的触摸机制中,接收者并不平等,较早处理事件的接收者有权停止事件的分发,使它不再继续传递给其他接收者。换句话说,带目标的触摸事件并不一定会被广播给所有的接收者。通常,游戏的菜单按钮、摇杆按钮等元件常使用目标触摸事件,以保证触摸事件不对其他层产生不良影响。
2.1标准触摸事件
经过前面的学习,我们知道利用CCLayer 可以方便地启用层上的标准触摸事件。实际上,并不是只有层才支持接收触摸事件,任何一个游戏元素都可以接受事件,只不过层中提供了现成的支持。下面我们将以层为例,来探究一下层是如何开启标准触摸事件的。
为了开启层的标准触摸事件支持,我们需要启用TouchEnable 属性。下面是CCLayer中与TouchEnable 属性相关的代码,为了清晰,我们已经去掉了与脚本引擎相关的语句:
void CCLayer::setTouchEnabled(bool enabled) { if (m_bIsTouchEnabled != enabled) { m_bIsTouchEnabled = enabled; if (m_bIsRunning) { if (enabled) { this->registerWithTouchDispatcher(); } else { CCDirector* pDirector = CCDirector::sharedDirector(); pDirector->getTouchDispatcher()->removeDelegate(this); } } } } void CCLayer::registerWithTouchDispatcher() { CCDirector* pDirector = CCDirector::sharedDirector(); pDirector->getTouchDispatcher()->addStandardDelegate(this,0); }
可以看到,如果设置开启触摸,则会调用registerWithTouchDispatcher 方法。而在registerWithTouchDispatcher 方法中,我们调用了 CCTouchDispatcher 的addStandardDelegate方法。在引擎中,CCTouchDispatcher 负责触摸事件的分发处理,此处的addStandardDelegate 方法会把当前对象注册到分发器中。被注册的对象必须实现 CCStandardTouchDelegate接口,当引擎从系统接收到触摸事件时,就会调用接口中对应的方法,触发触摸事件。
相比之下,关闭触摸则简单得多:只需要调用CCTouchDispatcher 的removeDelegate 方法即可。
因此可以总结,为了使一个对象接受标准触摸事件,主要有以下4 个步骤。
- 需要此对象实现CCStandardTouchDelegate 接口。(CCLayout默认实现,CCSprite可以自行实现)
- 使用addStandardDelegate 方法把自己注册给触摸事件分发器。
-
setTouchEnabled(true); void XXLayer::registerWithTouchDispatcher() { CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this,0,true);//addTargetedDelegate
}
-
- 重载事件回调函数,处理触摸事件。(begin,move,end,cancel 4个事件函数)
- 当不再需要接收触摸事件时,使用removeDelegate方法来注销触摸事件的接收。
Warn:关于标准触摸事件的警告和带目标的触摸事件的存在!
我们可以为任意对象添加标准触摸事件,然而如同前文所述,标准触摸事件中存在两个较为不便的地方,具体如下所示。
只要事件分发器接收到用户的触摸事件,就会分发给所有的订阅者,因此常常会出现按下按钮时,触摸事件穿透按钮分发给后面的层这种尴尬的情况。
当系统存在多个触摸点时,标准触摸事件会把所有触摸点都传递给回调函数,然而在许多情况下每个触摸点之间是独立的,屏幕上是否存在其他触摸点我们并不不关心,因此我们不必为了处理多个触摸事件手动遍历一遍触摸点。
为此,Cocos2d-x 为我们提供了一个简化的解决方案:带目标的触摸事件。与标准触摸事件类似,我们也需要首先使接受事件的对象实现一个接口CCTargetedTouchDelegate,然后把对象注册到触摸分发器中,最后当不再需要接受触摸事件时注销自身。
和CCStandardTouchDelegate 接口类似,为了接受触摸事件,我们同样需要实现下面所示的4 个回调函数:
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchCancelled(CCTouc h *pTouch, CCEvent *pEvent);
细心的读者应该注意到了,这个接口和CCStandardTouchDelegate 存在两处不同,这两处不同也对应着带目标的触摸事件的两个特点。
- 事件参数不再是集合,而是一次只传入一个触摸点。
- ccTouchBegin方法返回一个布尔值,表示声明是否要捕捉这个触摸点,只有在此方法中捕捉到的触摸点才会继续引发其他3 个事件,否则此触摸点的其他事件都会被忽略。
通过CCTargetedTouchDelegate 的方式接收触摸事件,就无法直接利用 CCLayer 提供的属性了,因此 我们必须主动把自身注册到引擎的触摸分发器:
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
在addTargetedDelegate 方法中,前两个参数分别对应触摸接收对象和优先级,其中优先级是一个整型参数,值越低,则优先级越高,也就越早获得触摸事件。通常,为了获得较高的优先级,可以将其指定为负数。第三个参数较为有趣,表明了是否" 吞噬" 一个触摸,如果设置为true,一个触摸一旦被捕捉,那么所有优先级更低的接收对象都无法接收到触摸。CCMenu就是一个会" 吞噬" 且优先级为-128 的触摸接收器,由于它的优先级很高,所以菜单按钮总能获得触摸响应。
综上所述,带目标的触摸事件的使用步骤如下所示。
- 实现CCTargetedTouchDelegate 接口。
- 使用addTargetedDelegate 方法注册到触摸事件分发器。
- 重载事件回调函数。注意,我们必须在触摸开始事件中针对需要接受的事件返回true以捕捉事件。
- 当不再需要接受触摸事件时,使用removeDelegate方法来注销触摸事件的接收。与标准触摸事件相比,不同之处主要在于开始触摸事件需要返回一个代表是否捕捉事件的值。
触摸分发器原理 && 触摸中的陷阱:http://www.cnblogs.com/TS-qrt/articles/TouchesEx.html
浙公网安备 33010602011771号