【转载】Quick 中的触摸事件
Quick 中的触摸事件
Cocos2d-x 原本的触摸机制存在一些限制,在使用中需要开发者做不少额外的处理。所以 Quick-Cocos2d-x 提出了自己的一套触摸机制。本文详细介绍了这套新机制的用法。
显示层级
在 Cocos2d-x 里,整个游戏的画面是由一系列的 Scene, Node, Sprite, Layer 等对象构成的。而所有这些对象都是从 Node 这个类继承而来。我们可以将 Node 称为显示节点
。
一个游戏画面就是许多显示节点构成的一棵树:
1
2
3
4
5
6
7
8
9
10
11
12
|
/|\ | 显示层级 | | [Node] [Node] [Node] | | | | | +---+---+ | | | | | [Node] [Node] | | | | +-----+-----+ | | | [Node] |
在这棵树里,Node 所处的垂直位置就是它们的显示层级
。越往上的 Node,其显示层级就越高。从画面表现上来说,下面的 Node 是背景,上面的 Node 是建筑,那么建筑就会挡住一部分背景。
触摸区域
在 Cocos2d-x 里,只有 Layer 对象才能接受触摸事件。而 Layer 总是响应整个屏幕范围内的触摸,这就要求开发者在拿到触摸事件后,再做进一步的处理。
例如有一个需求是在玩家触摸屏幕上的方块时,人物角色做一个动作。那么使用 Layer 接受到触摸事件后,开发者需要自行判断触摸位置是否在方块之内。当屏幕上有很多东西需要响应玩家交互时,程序结构就开始变得复杂了。
所以 Quick-Cocos2d-x 允许开发者将任何一个 Node 设置为接受触摸事件。并且触摸事件一开始只会出现在这个 Node 的触摸区域
内。
所谓触摸区域
,就是一个 Node 及其所有子 Node 显示内容占据的屏幕空间。要注意的是这个屏幕空间包含了图片的透明部分。下图中,节点 A 是一个 Sprite 对象,它的触摸区域就是图片大小;而节点 B 是一个 Node 对象,其中包含了三个 Sprite 对象,那么节点 B 的触摸区域就是三个 Sprite 对象触摸区域的合集。
为了简化实现,触摸区域
都是一个矩形,所以节点 B 的触摸区域
实际上是一个“包含三个 Sprite 对象触摸区域合集的矩形”,可以参考上图中的红色边框线。
用法示例
下面列出触摸事件的用法示例,更详细的示例请参考 samples/touch
示例。
单点触摸事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-- 允许 node 接受触摸事件 node:setTouchEnabled( true ) -- 注册触摸事件 node:addNodeEventListener(cc.NODE_TOUCH_EVENT, function(event) -- event.name 是触摸事件的状态:began, moved, ended, cancelled -- event.x, event.y 是触摸点当前位置 -- event.prevX, event.prevY 是触摸点之前的位置 printf ( "sprite: %s x,y: %0.2f, %0.2f" , event.name, event.x, event.y) -- 在 began 状态时,如果要让 Node 继续接收该触摸事件的状态变化 -- 则必须返回 true if event.name == "began" then return true end end) |
触摸事件的 event.name
指示了事件的状态:
- began: 手指开始触摸屏幕。在
began
状态时,如果要继续接收该触摸事件的状态变化,事件处理函数必须返回true
。 - moved: 手指在屏幕上移动。
- ended: 手指离开屏幕。
- cancelled: 因为其他原因取消触摸操作。
多点触摸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
-- 允许 node 接受触摸事件 node:setTouchEnabled( true ) -- 设置触摸模式 node:setTouchMode(cc.TOUCH_MODE_ALL_AT_ONCE) -- 多点 -- node:setTouchMode(cc.TOUCH_MODE_ONE_BY_ONE) -- 单点(默认模式) -- 注册触摸事件 node:addNodeEventListener(cc.NODE_TOUCH_EVENT, function(event) -- event.name 是触摸事件的状态:began, moved, ended, cancelled -- 多点触摸增加了 added 和 removed 状态 -- event.points 包含所有触摸点 -- 按照 events.point[id] = {x = ?, y = ?} 的结构组织 for id, point in pairs(event.points) do printf ( "event [%s] %s = %0.2f, %0.2f" , event.name, id, point.x, point.y) end if event.name == "began" then return true end end) |
在多点触摸时,事件状态的含义有所区别:
- began: 手指开始触摸屏幕。用户可能同时用多个手指接触屏幕,但因为硬件响应速度极快的原因,
began
状态时,event.points
中可能仍然只有一个触摸点的数据,其他触摸点数据会通过added
状态提供。 - added: 开始触摸后,如果有更多触摸点出现,则出现
added
状态。此时event.points
中包含新加入的触摸点数据。 - removed: 如果触摸结束前有触摸点消失(接触屏幕的部分手指离开了屏幕),则出现
removed
状态。此时event.points
中包含删除的触摸点数据。 - ended: 如果所有触摸点都消失(所有手指都离开了屏幕),则出现
ended
状态。此时event.points
中包含删除的触摸点数据。 - moved: 由于多点触摸时,可能只有部分触摸点移动。所以此时
event.points
中只包含有变化的触摸点数据。
触摸事件吞噬
默认情况下,Node 在响应触摸后(在 began
状态返回 true
表示要响应触摸),就会阻止事件继续传递给 Node 的父对象(更下层的 Node),这称为触摸事件吞噬
。
如果要改变这个行为,可以用:
- setTouchSwallowEnabled() 是否允许 Node 吞噬触摸,默认为
true
。如果设置为false
,则 Node 响应触摸事件后,仍然会将事件继续传递给父对象。 - isTouchSwallowEnabled() 检查 Node 是否允许吞噬触摸。
禁用触摸
对于一个 Node,随时可以启用或禁用其触摸事件:
- setTouchEnabled() 是否允许 Node 响应触摸,默认为
false
。 - isTouchEnabled() 检查 Node 是否允许触摸。
但即便禁用了 Node 的触摸事件,也只能阻止这个 Node 响应触摸,而不能阻止这个 Node 的子 Node 响应触摸。
假设有一个对话框(Node),我们需要禁止对话框中的所有 Node 响应触摸。那么需要禁止对话框 Node 捕获事件:
1
|
dialog:setTouchCaptureEnabled( false ) |
- setTouchCaptureEnabled() 是否允许 Node 捕获触摸,默认为
true
。当设置为false
时,该 Node 及其所有子 Node 都无法得到触摸事件。 - isTouchCaptureEnabled() 检查 Node 是否允许捕获触摸。
总结而言,setTouchEnabled()
只针对当前 Node,而 setTouchCaptureEnabled()
同时影响当前 Node 及其所有子 Node。
触摸事件的三个阶段
quick 中触摸事件分为三个阶段:capturing(捕获)、targeting(触发)、bubbling(冒泡)。
当用户的一根手指触摸到屏幕时,将产生一个触摸事件:
- 遍历所有响应触摸的 Node,找出
显示层级
最高,并且其触摸区域
包含触摸位置的那个 Node。这个 Node 被称为 TargetNode(目标 Node)。 - 检查 TargetNode 的
isTouchCaptureEnabled()
结果,如果返回false
,则重复 1- 从 TargetNode 的根 Node(通常是 Scene)开始,检查
cc.NODE_TOUCH_CAPTURE_EVENT
事件的返回结果。任何一个 Node 返回false
都会阻止事件在 TargetNode 上触发。并从步骤 1 开始查找其他符合条件的 Node。 - 这个阶段被称为
capturing
。
- 从 TargetNode 的根 Node(通常是 Scene)开始,检查
- 在 TargetNode 上触发事件。这个阶段被称为
targeting
。 - 如果事件返回结果为
false
,表示 TargetNode 不响应该事件,并从步骤 1 开始查找其他符合条件的 Node。 - 在 TargetNode 完成事件的响应后,检查
TargetNode:isTouchSwallowEnabled()
的返回值。如果是true
,则取消bubbling
阶段。 - 从 TargetNode 开始往其所有父 Node 触发事件,直到某个 Node 返回
false
或者事件被吞噬。这个阶段称为bubbling
。
利用事件的三个阶段,我们可以注册 capturing
阶段的触摸事件处理函数:
1
2
3
4
5
6
7
|
-- 在 capturing 阶段就捕获事件 node:addNodeEventListener(cc.NODE_TOUCH_CAPTURE_EVENT, function(event) if event.name == "began" then -- 在 began 状态返回 false ,将阻止事件 return false end end) |
关于触摸机制的灵活运用,可以参考 cc.ui
中的各个 UI 控件,以及 samples/touch
示例。
API 参考
- addNodeEventListener() 为 Node 的特定事件设置处理函数,返回一个 id 表示注册成功。
- removeNodeEventListener() 从 Node 上移除指定类型的事件处理函数,需要提供
addNodeEventListener()
返回的注册 id。 - setTouchEnabled() 是否允许 Node 响应触摸,默认为
false
。 - isTouchEnabled() 检查 Node 是否允许触摸。
- setTouchMode() 设置触摸模式,默认为
cc.TOUCH_MODE_ONE_BY_ONE
。 - getTouchMode() 返回 Node 当前的触摸模式。
- setTouchCaptureEnabled() 是否允许 Node 捕获触摸,默认为
true
。 - isTouchCaptureEnabled() 检查 Node 是否允许捕获触摸。
- setTouchSwallowEnabled() 是否允许 Node 吞噬触摸,默认为
true
。 - isTouchSwallowEnabled() 检查 Node 是否允许吞噬触摸。
【转载】Quick 中的触摸事件的更多相关文章
- 关于js-binding中Layer触摸事件的优化
关于js-binding中Layer触摸事件的优化 cocos2d-x 3.7 1. 目前js中监听触摸事件带来的不便(特别是cocosbuilder) 在目前的js-binding中,如果要监听la ...
- iOS中的触摸事件和手势处理
iOS中的事件可以分为三大类: 1> 触摸事件 2> 加速计事件 3> 远程控制事件 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并 ...
- cocos2dx中的触摸事件及触摸优先级
1.只有CCLayer及其派生类才有触摸功能. 2.开启触摸 setTouchEnable(true); 3.设置触摸模式,单点,多点(仅IOS支持) setTouchMode(kCCTouchesO ...
- iOS中的触摸事件,手势识别,摇晃事件等
在iOS中,事件可以划分为以下几类: 1.触摸事件:通过触摸,手势进行触发(手指点击.缩放等) 2.运动事件:通过加速器触发(例如手机晃动) 3.远程控制事件:通过其他远程设备触发(例如耳机控制按钮) ...
- 浅析iOS中的触摸事件
一.什么是响应者对象? 在 iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为“响应者对象”.UIApplication.UIViewControl ...
- VIew中的触摸事件 touchBegin 等一系列方法
5.触摸事件 touchBegin 等一系列方法 1)手指按下 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; 2 ...
- 转载 -- C# 中的委托和事件
原文地址:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx C# 中的委托和事件 引言 委 ...
- react-native中的触摸事件
移动应用上的用户交互基本靠"摸".当然,"摸"也是有各种姿势的:在一个按钮上点击,在一个列表上滑动, 或是在一个地图上缩放.React Native 提供了可以 ...
- vue中使用触摸事件,上滑,下滑,等
第一步,下载一个包 npm install kim-vue-touch -s 在当前项目中下载包 第二部 import vueTouch from 'kim-vue-touch' Vue.use(vu ...
随机推荐
- LintCode-376.二叉树的路径和
二叉树的路径和 给定一个二叉树,找出所有路径中各节点相加总和等于给定 目标值 的路径. 一个有效的路径,指的是从根节点到叶节点的路径. 样例 给定一个二叉树,和 目标值 = 5: 返回: [ ...
- jquery.validate 中文乱码解决方法
第一种.就是所说的引用jquery.validate.messages_cn.js 下载地址:http://files.cnblogs.com/files/pin ...
- 软工网络15团队作业4——Alpha阶段敏捷冲刺-2
一.当天站立式会议照片: 二.项目进展 昨天已完成的工作: 微信公众号平台注册"小程序"的账号; 下载微信官方的小程序开发工具,这个是编辑小程序和上传审核小程序必须的工具; 大家一 ...
- PAT 1058 选择题
https://pintia.cn/problem-sets/994805260223102976/problems/994805270356541440 批改多选题是比较麻烦的事情,本题就请你写个程 ...
- VUE01指令
一.下载Vue2.0的两个版本: 官方网站:http://vuejs.org/ 开发版本:包含完整的警告和调试模式 生产版本:删除了警告,进行了压缩 二.项目结构搭建 这个部分要视频中有详细讲解. 三 ...
- 简述在akka中发送消息的过程
在flink的数据传输过程中,有两类数据,一类数据是控制流数据,比如提交作业,比如连接jm,另一类数据是业务数据.flink对此采用了不同的传输机制,控制流数据的传输采用akka进行,业务类数据传输在 ...
- WASM
WASM WebAssembly https://webassembly.org/ https://github.com/appcypher/awesome-wasm-langs https://me ...
- 分布式消息队列RocketMQ&Kafka -- 消息的“顺序消费”
在说到消息中间件的时候,我们通常都会谈到一个特性:消息的顺序消费问题.这个问题看起来很简单:Producer发送消息1, 2, 3... Consumer按1, 2, 3...顺序消费. 但实际情况却 ...
- Lucene笔记一
Lucene就是一个全文检索的工具,建立索引用的,类似于新华字典的目录 这里使用的是lucene-4.4.0版本,入门代码所需jar包如下图所示(解压lucene-4.4.0后的目录): 入门代码: ...
- Python列表去重的三种方法
1. 列表去重 li = [] for item in my_list: if item not in li: li.append(item) 2.集合去重 list(set(my_list)) 3. ...