quick-cocos2d-x 学习系列之十三 触摸
现在智能机基本都是触摸屏,除了键盘爱好者们耍键盘。我们要通过这小小的触摸屏上完成整个游戏逻辑的控制,需要对这巴掌大地方进行详细控制了。
1.单点触摸测试
创建精灵函数
function createTouchableSprite(p)
local sprite = display.newScale9Sprite(p.image)
sprite:setContentSize(p.size)
local cs = sprite:getContentSize()
local label = cc.ui.UILabel.new({
UILabelType = 2,
text = p.label,
color = p.labelColor})
label:align(display.CENTER)
label:setPosition(cs.width / 2,label:getContentSize().height)
sprite:addChild(label)
sprite.label = label
return sprite
end
划Box框
function drawBoundingBox(parent,target,color)
local cbb = target:getCascadeBoundingBox()
local left,bottom, width,height = cbb.origin.x,cbb.origin.y, cbb.size.width,cbb.size.height
local points = {
{left, bottom},
{left + width,bottom},bottom + height},
{left, bottom + height},
}
local Box = display.newPolygon(points,{borderColor = color})
parent:addChild(Box,1000)
end
1.1响应触摸事件
self.sprite = createTouchableSprite({
image = "WhiteButton.png",
size = cc.size(500,300),
label = "TOUCH ME !",
labelColor = cc.c3b(255,0,0)})
:pos(display.cx,display.cy)
:addTo(self)
drawBoundingBox(self, self.sprite,cc.c4f(0,1.0, 1.0))
-- 启用触摸
self.sprite:setTouchEnabled(true)
self.sprite:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event)
-- event.name 是触摸事件的状态:began,moved,ended,cancelled
-- event.x,event.y 是触摸点当前位置
-- event.prevX,event.prevY 是触摸点之前的位置
local label = string.format("sprite: %s x,y: %0.2f,%0.2f",event.name,event.x,event.y)
self.sprite.label:setString(label)
-- 返回 true 表示要响应该触摸事件,并继续接收该触摸事件的状态变化
return true
end)
单点触摸是最直接的使用方式了。
1.2事件穿透和事件捕获
--创建底层触摸层
self.parentButton = createTouchableSprite({
image = "WhiteButton.png",
size = cc.size(600,500),display.cy)
:addTo(self)
self.parentButton.name = "parentButton"
drawBoundingBox(self,self.parentButton, 1.0))
self.parentButton:setTouchEnabled(true)
--给该触摸层增加监听
self.parentButton:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event)
local label = string.format("parentButton: %s x,event.y)
self.parentButton.label:setString(label)
return true
end)
--在底层创建button1,button1 响应触摸后,会吞噬掉触摸事件
self.button1 = createTouchableSprite({
image = "GreenButton.png",
size = cc.size(400,160),
label = "TOUCH ME !"})
:pos(300, 400)
:addTo(self.parentButton)
cc.ui.UILabel.new({text = "SWALLOW = YES\n事件在当前对象处理后被吞噬",size = 24})
:align(display.CENTER, 200,90)
:addTo(self.button1)
drawBoundingBox(self, self.button1,cc.c4f(1.0, 1.0))
self.button1:setTouchEnabled(true)
self.button1:setTouchSwallowEnabled(true)-- 是否吞噬事件,默认值为 true
self.button1:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event)
local label = string.format("button1: %s x,event.y)
self.button1.label:setString(label)
return true
end)
-- 在底层创建button2,响应触摸后,不会吞噬掉触摸事件
self.button2 = createTouchableSprite({
image = "PinkButton.png", 200)
:addTo(self.parentButton)
cc.ui.UILabel.new({text = "SWALLOW = NO\n事件会传递到下层对象",90)
:addTo(self.button2)
drawBoundingBox(self,self.button2, 1.0))
self.button2:setTouchEnabled(true)
self.button2:setTouchSwallowEnabled(false) -- 当不吞噬事件时,触摸事件会从上层对象往下层对象传递,称为“穿透”
self.button2:addNodeEventListener(cc.NODE_TOUCH_EVENT,event.y)
self.button2.label:setString(label)
return true
end)
事件穿透通过函数setTouchSwallowEnabled来实现。
如下函数设置是否捕捉触摸
self.parentButton:setTouchCaptureEnabled(button:isButtonSelected())
1.3在事件捕获阶段决定是否接受事件
-- 这个标志变量用于在触摸事件捕获阶段决定是否接受事件
self.isTouchCaptureEnabled_ = true
--parentButton 是 button1 的父节点
self.parentButton = createTouchableSprite({
image = "WhiteButton.png",display.cy)
:addTo(self)
drawBoundingBox(self, 1.0))
self.parentButton.label2 = cc.ui.UILabel.new({text = "",size = 24,color = cc.c3b(0,255)})
:align(display.CENTER, 300,60)
:addTo(self.parentButton)
self.parentButton:setTouchEnabled(true)
self.parentButton:addNodeEventListener(cc.NODE_TOUCH_EVENT,event.y)
self.parentButton.label:setString(label)
printf("%s %s [TARGETING]","parentButton",event.name)
if event.name == "ended" or event.name == "cancelled" then
print("-----------------------------")
else
print("")
end
return true
end)
-- 可以动态捕获触摸事件,并在捕获触摸事件开始时决定是否接受此次事件
self.parentButton:addNodeEventListener(cc.NODE_TOUCH_CAPTURE_EVENT,function(event)
if event.name == "began" then
print("-----------------------------")
end
local label = string.format("parentButton CAPTURE: %s x,event.y)
self.parentButton.label2:setString(label)
printf("%s %s [CAPTURING]",event.name)
if event.name == "began" or event.name == "moved" then
return self.isTouchCaptureEnabled_
end
end)
-- button1响应触摸后,会吞噬掉触摸事件
self.button1 = createTouchableSprite({
image = "GreenButton.png", 400)
:addTo(self.parentButton)
cc.ui.UILabel.new({text = "SWALLOW = YES\n事件在当前对象处理后被吞噬",90)
:addTo(self.button1)
drawBoundingBox(self,self.button1, 1.0))
self.button1:setTouchEnabled(true)
self.button1:setTouchSwallowEnabled(true) -- 是否吞噬事件,默认值为 true
self.button1:addNodeEventListener(cc.NODE_TOUCH_EVENT,event.y)
self.button1.label:setString(label)
printf("%s %s [TARGETING]","button1",event.name)
if event.name == "ended" or event.name == "cancelled" then
print("-----------------------------")
else
print("")
end
return true
end)
-- button2响应触摸后,不会吞噬掉触摸事件
self.button2 = createTouchableSprite({
image = "PinkButton.png",event.y)
self.button2.label:setString(label)
printf("%s %s [TARGETING]","button2",event.name)
return true
end)
-- 即便父对象在捕获阶段阻止响应事件,但子对象仍然可以捕获到事件,只是不会触发事件
self.button2:addNodeEventListener(cc.NODE_TOUCH_CAPTURE_EVENT,function(event)
printf("%s %s [CAPTURING]",event.name)
return true
end)
-- 放置一个开关按钮在屏幕上
local labels = {}
labels[true] = "父对象【可以】捕获触摸事件"
labels[false] = "父对象【不能】捕获触摸事件"
local images = {on = "CheckBoxButton2On.png",off = "CheckBoxButton2Off.png"}
self.captureEnabledButton = cc.ui.UICheckBoxButton.new(images)
:setButtonLabel(cc.ui.UILabel.new({text= labels[true],size = 24}))
:setButtonLabelOffset(40,0)
:setButtonSelected(true)
:onButtonStateChanged(function(event)
local button = event.target
button:setButtonLabelString(labels[button:isButtonSelected()])
end)
:onButtonClicked(function(event)
local button = event.target
self.isTouchCaptureEnabled_ = button:isButtonSelected()
end)
:pos(display.cx - 160,display.top- 80)
:addTo(self)
cc.ui.UILabel.new({
text = "事件处理流程:\n1. 【捕获】阶段:从父到子\n2. 【目标】阶段\n3. 【传递】阶段:尝试传递给下层对象",
size= 24})
:align(display.CENTER_TOP,display.cx,display.top - 120)
:addTo(self)
其中NODE_TOUCH_EVENT和 NODE_TOUCH_CAPTURE_EVENT 表示两种事件。
在NODE_TOUCH_CAPTURE_EVENT的处理函数中返回真假,然后决定是否调用NODE_TOUCH_EVENT的处理函数。
1.4容器的触摸区域由子对象决定
创建一个node,在node上增加几个精灵,精灵的局域决定的触摸的范围。
-- touchableNode 是启用触摸的 Node
self.touchableNode = display.newNode()
self.touchableNode:setPosition(display.cx,display.cy)
self:addChild(self.touchableNode)
-- 在 touchableNode 中加入一些 sprite
local count = math.random(3,8)
local images = {"WhiteButton.png", "BlueButton.png","GreenButton.png", "PinkButton.png"}
for i = 1,count do
local sprite = display.newScale9Sprite(images[math.random(1,4)])
sprite:setContentSize(cc.size(math.random(100,200),math.random(100,200)))
sprite:setPosition(math.random(-200,math.random(-200,200))
self.touchableNode:addChild(sprite)
end
self.stateLabel = cc.ui.UILabel.new({text = ""})
self.stateLabel:align(display.CENTER,display.top - 100)
self:addChild(self.stateLabel)
-- 启用触摸
self.touchableNode:setTouchEnabled(true)
self.touchableNode:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event)
local label = string.format("touchableNode: %s x,event.y)
self.stateLabel:setString(label)
return true
end)
drawBoundingBox(self, self.touchableNode, 1.0))
2.多点触摸
2.1响应触摸事件
-- createTouchableSprite() 定义在 includes/functions.lua 中
self.sprite = createTouchableSprite({
image = "WhiteButton.png",600),self.sprite, 1.0))
local labelPoints = cc.ui.UILabel.new({text = "",size = 24})
:align(display.CENTER_TOP,display.top - 120)
:addTo(self)
-- 启用触摸
self.sprite:setTouchEnabled(true)
-- 设置触摸模式
self.sprite:setTouchMode(cc.TOUCH_MODE_ALL_AT_ONCE)-- 多点
--self.sprite:setTouchMode(cc.TOUCH_MODE_ONE_BY_ONE) -- 单点(默认模式)
self.sprite:addNodeEventListener(cc.NODE_TOUCH_EVENT,cancelled,added(仅限多点触摸),removed(仅限多点触摸)
-- event.points 包含所有触摸点,按照 events.point[id] = {x = ?,y = ?} 的结构组织
local str = {}
for id,point in pairs(event.points) do
str[#str + 1] = string.format("id: %s,x: %0.2f,y: %0.2f",point.id,point.x,point.y)
end
local pointsCount = #str
table.sort(str)
labelPoints:setString(table.concat(str,"\n"))
if event.name == "began" or event.name == "added" then
self.touchIndex = self.touchIndex + 1
for id,point in pairs(event.points) do
local cursor = display.newSprite("Cursor.png")
:pos(point.x,point.y)
:scale(1.2)
:addTo(self)
self.cursors[id] = cursor
end
elseif event.name == "moved" then
for id,point in pairs(event.points) do
local cursor = self.cursors[id]
local rect = self.sprite:getBoundingBox()
if cc.rectContainsPoint(rect,cc.p(point.x, point.y)) then
-- 检查触摸点的位置是否在矩形内
cursor:setPosition(point.x,point.y)
cursor:setVisible(true)
else
cursor:setVisible(false)
end
end
elseif event.name == "removed" then
for id,point in pairs(event.points) do
self.cursors[id]:removeSelf()
self.cursors[id] = nil
end
else
for _,cursor in pairs(self.cursors) do
cursor:removeSelf()
end
self.cursors = {}
end
local label = string.format("sprite: %s,count = %d,index = %d",pointsCount,self.touchIndex)
self.sprite.label:setString(label)
if event.name == "ended" or event.name == "cancelled" then
self.sprite.label:setString("")
labelPoints:setString("")
end
-- 返回 true 表示要响应该触摸事件,并继续接收该触摸事件的状态变化
return true
end)
cc.ui.UILabel.new({
text = "注册多点触摸后,目标将收到所有触摸点的数据\nadded 和 removed 指示触摸点的加入和移除",
size= 24})
:align(display.CENTER,display.top - 80)
:addTo(self)
2.2在事件捕获阶段决定是否接受事件
-- 这个标志变量用于在触摸事件捕获阶段决定是否接受事件
self.isTouchCaptureEnabled_ = true
--parentButton 是 button1 的父节点
self.parentButton = createTouchableSprite({
image = "WhiteButton.png",function(event)
local label = string.format("parentButton: %s",event.name)
self.parentButton.label:setString(label)
printf("%s %s [TARGETING]",function(event)
if event.name == "began" then
print("-----------------------------")
end
local label = string.format("parentButton CAPTURE: %s",event.name)
self.parentButton.label2:setString(label)
printf("%s %s [CAPTURING]", 1.0))
self.button1:setTouchEnabled(true)
self.button1:setTouchMode(cc.TOUCH_MODE_ALL_AT_ONCE)-- 多点
self.button1:setTouchSwallowEnabled(true) -- 是否吞噬事件,默认值为 true
self.button1:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event)
local label = string.format("button1: %s count: %d",table.nums(event.points))
self.button1.label:setString(label)
printf("%s %s [TARGETING]", 1.0))
self.button2:setTouchEnabled(true)
self.button2:setTouchMode(cc.TOUCH_MODE_ALL_AT_ONCE)-- 多点
self.button2:setTouchSwallowEnabled(false) -- 当不吞噬事件时,触摸事件会从上层对象往下层对象传递,称为“穿透”
self.button2:addNodeEventListener(cc.NODE_TOUCH_EVENT,table.nums(event.points))
self.button2.label:setString(label)
printf("%s %s [TARGETING]",display.top - 120)
:addTo(self)
2.3容器的触摸区域由子对象决定
-- touchableNode 是启用触摸的 Node
self.touchableNode = display.newNode()
self.touchableNode:setPosition(display.cx,display.top - 100)
self:addChild(self.stateLabel)
-- 启用触摸
self.touchableNode:setTouchEnabled(true)
self.touchableNode:setTouchMode(cc.TOUCH_MODE_ALL_AT_ONCE)-- 多点
self.touchableNode:addNodeEventListener(cc.NODE_TOUCH_EVENT,function(event)
local str = {}
for id,point.y)
end
self.stateLabel:setString(table.concat(str,"\n"))
return true
end)
drawBoundingBox(self,self.touchableNode, 1.0))
--
app:createNextButton(self)
app:createTitle(self, "多点触摸测试 - 容器的触摸区域由子对象决定")