cocos2d-x 详解之 CCLayer(触摸事件)
CCLayer继承自CCNode,在CCLayer中可以实现单点触摸、多点触摸和重力感应回调3种不同形式的交互。这部分的难点在于,当存在多个层都要去接收触摸时它的响应机制是如何处理的。了解内部的处理机制使用起来才会得心应手,说简单也难,说难也简单,让我们一点点剖析。
从触摸方式上看,触摸分两种类型:单点触摸 和多点触摸。
从触摸事件的响应机制上看,触摸事件又分为:标准触摸事件 和目标触摸事件。
引擎当中处理用户操作的机制是一种由事件驱动的处理机制。所谓的事件驱动,就是指只有当事件发生了,才会执行对应的机制。在引擎当中,用户操作将会经过”接受“、”分发“、”处理“三个步骤,我们先说接收操作。
(一)接收操作
实际上,无论运行在什么设备平台,运行平台所接受的用户操作数据都会保存为统一的数据格式,然后转化为引擎需要的用户操作信息,因此引擎专门设计了一个用来存储用户操作数据的类CCTouch,这个类中保存了用户的操作信息(因此我们可以简单的理解为CCTouch就是信息):
CCTouch();//构造函数
CCPoint locationInView();//用户触摸的视图坐标
CCPoint previousLocationInView();//前一次用户触摸的视图坐标
void setTouchInfo(int id,float x,float y);//设置用户操作数据(id标识,x坐标,y坐标)
int getId() const;//返回id标识
至于平台如何接收用户操作数据的,请参考CCEGLViewProtoco.cpp文件中的handleTouchesBegin()函数。
(二)分发机制
分发机制解释了引擎获得了用户的操作数据后是如何将信息传递到能接收响应的层或其他节点。
引擎专门提供了一个负责分发触摸消息的类:分发器--CCTouchDispatcher,它会向所有需要响应的对象发送用户操作信息,那么问题就来了,分发器是如何知道该把信息发给谁呢,即都有谁是需要响应的的对象呢。于是引擎又专门设计了一个触摸代理类CCTouchDelete,凡是实现了触摸代理CCTouchDelete的类都是可响应的对象。CCTouchDelete有三个子类,我们自定义的节点如果要能够接收用户操作通常都要继承这三个子类之一,实现了代理以后,最关键的是还要在分发器中注册,这样分发器才能找到它。
CCStandardTouchDelegate用于处理多点触摸;CCTargetedTouchDelegate用于处理单点触摸。
这里面提到了标准触摸代理和目标触摸代理,对应的就是标准触摸事件和目标触摸事件,下面总结一下:
首先,区分一下“标准触摸事件”和“目标触摸事件”
1)标准触摸事件(通常对应“多点触摸”):即是默认的接收触摸模式。比如CCLayer,它实现了CCTouchDelegate代理,并在onEnter()中注册了标准触摸事件。通常我们自定义一个层继承自CCLayer并且开启触摸(setTouchEnable(true))的话就默认为标准触摸事件,不再需要手动实现代理和注册,用起来比较简单。它的特点是:如果场景中存在多个层,那么他们都会响应触摸,就会出现触摸事件在多个层之间的传递。有时候我们并不希望点击最上层的时候下面的层也跟着响应,这就需要目标触摸事件。
2)目标触摸事件(通常对应“单点触摸”):触摸事件的接收者并不平等,具有优先级,并且有权停止事件的分发,使它不再继续传递给其他接收者而独自吞下了这个触摸事件,这里的接受者就是指定的响应目标。目标代理对象是用来响应单点触摸的,常用在比较小的对象之上,比如一个精灵或者一个按钮。玩家是很难再一个按钮上进行多点触碰操作的,而与之对应的标准代理对象则是用在触碰面积较大的层对象之上。
通常,游戏的菜单按钮、摇杆按钮等元件常使用目标触摸事件,以保证触摸事件不对其他层产生不良影响。要实现目标触摸,则要实现目标触摸代理CCTargetedTouchDelegate,并手动注册触摸事件:
//第二个参数是代理的优先级,如果数值越小优先级越高
//第三个参数是true表示当前图层处理消息后不再进行消息的后续传递,截断触摸消息
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0, true);
(三)处理响应
再来区分一下单点触摸和多点触摸:
多点触碰是可以包含单电触碰的,是一个从多到一的过程。显而易见,多点触摸的处理中必然包含了单点触摸,但是单点触摸却无法实现如捏合、分离的用户操作。
1) 单点触摸
要使用单点触摸,必须要重写以下几个接口:
virtual void onEnter();//必须,并且通常在这里注册触摸事件
virtual void onExit();//必须,并且通常在这里移除触摸代理事件
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);//在控件上按住滑动到控件外抬起触摸时响应,可选,但一般情况下不需要
注意:ccTouchBegan();该接口,细心的人可能会发现,就它是返回bool的。它的返回值决定着,后续的ccTouchMoved();ccTouchEnded();ccTouchCancelled();是否触发。只有返回true时才触发。使用时,如果要监听目标触摸事件,别忘了注册:
//当自定义层进入场景时调用
void MyLayer::onEnter()
{
CCLayer::onEnter(); //最好写上
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true); //注册目标触摸事件
} //当自定义层退出场景时调用
void MyLayer::onExit()
{
CCLayer::onExit(); //最好写上
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->removeDelegate(this);
}
//因为委托代理一般都是成对出现的,有注册就要对应有移除,若不移除,则有可能造成程序的异常退出。一般情况下,推荐大家将触屏事件的注册于移除代码都写在onEnter于onExit中。
2)多点触摸
CCLayer默认已经帮我们实现了。要使用多点触摸则只需要实现重写如下几个接口即可
//同单点触摸类似,但是不需要onEnter,但需要写onExit
virtual void registerWithTouchDispatcher(void);//多触点的委托注册放在onEnter的生命函数中会造成程序异常退出,默认都重写此函数,将多触点委托注册放在此函数中进行注册
virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
virtual void onExit();//在这里移除注册
注意:CCSet*对象,是触摸点的集合。它存储着所有的触摸信息。即:遍历它,可以处理所有的触摸点响应。其他的用法同单点触摸一样。
接下来就是接收触摸后的处理:
常用触摸点坐标转换方式:
CCPoint point=pTouch->getLocation(); //通过pTouch得到用户的触摸点,getLocation函数会将触摸坐标转换为GL坐标,因此拿到的坐标可以直接使用
CCPoint ptNode = convertTouchToNodeSpace(pTouch);//将视图坐标转化为节点坐标(节点坐标等同于绘图坐标,即GL坐标),原点在本节点的左下角
CCPoint ptNode = convertTouchToNodeSpaceAR(pTouch);//转换为以锚点为原点的坐标系中的坐标
多点触摸使用实例:
//注册多触点的委托监听
void HelloWorld::registerWithTouchDispatcher(void){
CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this, 0);
} //用户手指第一次触碰
void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent){
CCSetIterator iter = pTouches->begin();//CCSetIterator:迭代器,用于遍历CCSet集合对象
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint location = pTouch->getLocation();
if(pTouch->getID()==0){//第一个触点
CCSprite * sp1 = (CCSprite*)this->getChildByTag(91);
sp1->setPosition(location);
}else if(pTouch->getID()==1){//第二个触点
CCSprite * sp2= (CCSprite*)this->getChildByTag(92);
sp2->setPosition(location);
}
}
} //用户手指进行移动或者拖拽
void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent){
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint location = pTouch->getLocation();
if(pTouch->getID()==0){//第一个触点
CCSprite * sp1 = (CCSprite*)this->getChildByTag(91);
sp1->setPosition(location);
}else if(pTouch->getID()==1){//第二个触点
CCSprite * sp2= (CCSprite*)this->getChildByTag(92);
sp2->setPosition(location);
}
}
} //用户手指抬起
void HelloWorld::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent){
CCSetIterator iter = pTouches->begin();
for (; iter != pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint location = pTouch->getLocation();
CCLOG("pTouch 触摸点 %i 的坐标: x:%f,y:%f",pTouch->getID(),location.x,location.y);
}
} //删除多触点的委托监听
void HelloWorld::onExit(){
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit();
}
cocos2d-x 详解之 CCLayer(触摸事件)的更多相关文章
- jQuery:详解jQuery中的事件(二)
上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...
- 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码
详解C#泛型(二) 一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...
- 详解Vue 方法与事件处理器
本篇文章主要介绍了详解Vue 方法与事件处理器 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 方法与事件处理器 方法处理器 可以用 v-on 指令监听 DOM 事件 ...
- Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...
- ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)
前面的六篇文章,我们已经讨论了dapm关于动态电源管理的有关知识,包括widget的创建和初始化,widget之间的连接以及widget的上下电顺序等等.本章我们准备讨论dapm框架中的另一个机制:事 ...
- jQuery:详解jQuery中的事件(一)
之前用过一些jQuery的动画和特效,但是用到的部分也不超过10%的样子,感觉好浪费啊——当然浪费的不是jQuery,而是Web资源.后来就想深入研究下jQuery的内部机理,读过两遍jQuery源代 ...
- 详解C#委托,事件与回调函数
.Net编程中最经常用的元素,事件必然是其中之一.无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等.“protected void Pag ...
- localStorage、sessionStorage详解,以及storage事件使用
有关localStorage和sessionStorage的特性. localStorage本身带有方法有 添加键值对:localStorage.setItem(key,value),如果key存在时 ...
- 详解C#委托和事件(一)
委托(Delegate)是安全封装方法的类型,类似于C和C++中的函数指针,与函数指针不同的是,委托是面向对象的.类型安全的和可靠的: 一.委托类型是CTS中五种基础类型之一,是一种引用类型,表示对具 ...
随机推荐
- 将集成spring的项目从tomcat上移植到weblogic下存在的问题
当在weblogic下部署时, 1.需要jersey-servlet-xx.jar,jersey-core-xx.jar,jersey-server-xx.jar: 2.在web.xml中全局参数co ...
- 转:samba 启动和重新启动 以及在虚拟系统和实际系统怎么实现软件交换
转自:http://blog.csdn.net/zwhfyy/article/details/1605151 启动 smb start 重新启动 root 用户登陆 CHQ_WEB:/etc/init ...
- 用DateTime.ToString(string format)输出不同格式的日期
http://www.cnblogs.com/xvqm00/archive/2009/02/19/1394093.html DateTime.ToString()函数有四个重载.一般用得多的就是不带参 ...
- static int和static final int的区别
1.static变量 按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量:另一种是没有被static修饰的变量,叫实例变量.两者的区别是: 对于静态变量在内 ...
- 动态调整对话框属性(去掉标题栏,去掉边框,修改类似成Border:NONE样式)(调用ModifyStyle和ModifyStyleEx,然后调用SetWindowPos重新显示)
// 动态修改对话框属性,去掉对话框标题栏,设置Border为NONE属性. if(dlg.GetSafeHwnd()) { dlg.ModifyStyle(WS_CAPTION, 0); // 去掉 ...
- Android TextView多行文本滚动实现
Android中我们为了实现文本的滚动可以在ScrollView中嵌入一个TextView,其实TextView自己也可以实现多行滚动的,毕竟ScrollView必须只能有一个直接的子类布局.只要在l ...
- NDK xxxxx could not be resolved解决方法
Type '*****' could not be resolved Method '******' could not be resolved 问题解决 以下为未尝试方法,如果上面方法解 ...
- Java Swing中Substance个人比较喜欢的两种组合
try { // 设置外形装饰为可装饰 JFrame.setDefaultLookAndFeelDecorated(true); // 设置外观 UIManager.setLookAndFeel(ne ...
- JdbcTemplate查询数据 三种callback之间的区别
JdbcTemplate针对数据查询提供了多个重载的模板方法,你可以根据需要选用不同的模板方法. 如果你的查询很简单,仅仅是传入相应SQL或者相关参数,然后取得一个单一的结果,那么你可以选择如下一组便 ...
- Java 并发基础
Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...