前面介绍了ClippingNode的简单使用,补充说明setAlphaThreshold方法设定alpha阈值,只有模板(stencil)中像素的alpha值大于alpha阈值时,内容才会被绘制。默认为1,当为1时,模板全部绘制。若不是1,表示只绘制模板中alpha像素大于阈值的内容。
所以当setAlphaThreshold(1.0f)时,stencil全部绘制(红框内的全部),setInverted(false)显示被模板裁剪下来的底板内容是一个右图大小的草地图片,setInverted(true)显示剩余部分。
结合之前用到的屏蔽触摸的层,做一个简易的新手引导。
首先在MainScene中添加了2个Button
function MainScene:createUI()
local _size = cc.Director:getInstance():getWinSize()
local function btnClicked(sender,_type)
print('--btn click--')
end
local btn = ccui.Button:create('res/DS07.png','res/DS08.png','res/DS09.png')
self:addChild(btn)
btn:setPosition(_size.width/3,_size.height/3)
btn:addTouchEventListener(btnClicked)
local function btnClicked2(sender,_type)
print('--btn2 click--')
end
local btn2 = ccui.Button:create('res/DS10.png','res/DS11.png','res/DS12.png')
self:addChild(btn2)
btn2:setPosition(_size.width/3,_size.height/4.5)
btn2:addTouchEventListener(btnClicked2)
end
在新手引导时,需要玩家去点击Auto按钮,其他可以互动的控件全部不能响应,而且需要在视觉上将Auto按钮所在地方点亮,其他地方蒙蔽,那么可以结合之前的ClippingNode和屏蔽触摸层来实现。
新建一个Lua文件,代码:
local GuideShade = class('GuideShade',function () return cc.Node:create() end)
function GuideShade:ctor(x,y)
self.x = x
self.y = y
self:createUI()
self:openTouch()
end
function GuideShade:createUI()
local stencil = cc.Sprite:create('res/mask.png')
self.r = stencil:getContentSize().width / 2
local clipping = cc.ClippingNode:create(stencil)
clipping:setInverted(true)
clipping:setAlphaThreshold(0.3)
self:addChild(clipping,-1)
clipping:setPosition(self.x,self.y)
local pb = require("app/views/Pingbilayer").new()
clipping:addChild(pb)
pb:setPosition(-self.x,-self.y)
end
function GuideShade:checkPointInCircle(c_x,c_y)
local d_x = math.abs(c_x - self.x)
local d_y = math.abs(c_y - self.y)
local distance = math.pow(d_x,2) + math.pow(d_y,2)
if math.sqrt(distance) < self.r then
return true
end
return false
end
function GuideShade:openTouch()
local listener = cc.EventListenerTouchOneByOne:create()
local contain = false
local function onTouchBegan( touch,event )
local pos = touch:getLocation()
if self:checkPointInCircle(pos.x,pos.y) then
contain = true
else
contain = false
end
return true
end
local function onTouchMoved( touch,event )
end
local function onTouchEnded( touch,event )
local pos = touch:getLocation()
if contain and self:checkPointInCircle(pos.x,pos.y) then
print('--in circle--')
end
end
listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)
listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)
listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)
self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener,self)
local function onNodeEvent(event)
if event == 'exit' then
self:getEventDispatcher():removeEventListener(listener)
end
end
self:registerScriptHandler(onNodeEvent)
end
return GuideShade
在createUI方法中用到的模板:,所以点亮的地方是一个圆形,参数x,y为这个圆在世界坐标系中的位置。剪裁节点添加了屏蔽触摸的层,那么在GuideShade这个Node之下的控件将无法监听到触摸事件。
将GuideShade添加到MainScene中
function MainScene:onEnter() local _size = cc.Director:getInstance():getWinSize() local sp=cc.Sprite:create('res/HelloWorld.png') self:addChild(sp) sp:setPosition(_size.width/2,_size.height/2) self:createUI() self:openTouch() self:guide(_size) end function MainScene:guide(_size) local guideLayer = require('app/views/GuideShade').new(_size.width/3,_size.height/3) self:addChild(guideLayer) end
由于屏蔽了触摸,2个按钮都不能点击,function GuideShade:checkPointInCircle(c_x,c_y)判断了点击点是否在圆内,只有当触摸开始的点在圆内以及结束点也在圆内时,输出了–in circle–。
现在视觉上除了Auto按钮上方的圆点亮之外,其他地方都蒙灰了,且点击圆之外的地方,都没有反应(屏蔽层有反应),但是点击圆内的位置也没有反应,所以需要在引导时将要引导的数据传递过来,在点击圆内时响应原本Auto按钮的事件。
下面是MainScene中添加了模拟数据后的完整代码:
local MainScene = class("MainScene", cc.load("mvc").ViewBase)
local _data = {['1'] = {normal = nil,pressed = nil,disabled = nil,event = nil,name = nil},
['2'] = {normal = nil,pressed = nil,disabled = nil,event = nil,name = nil}}
function MainScene:ctor()
self:enableNodeEvents()
end
function MainScene:onEnter()
local _size = cc.Director:getInstance():getWinSize()
local sp=cc.Sprite:create('res/HelloWorld.png')
self:addChild(sp)
sp:setPosition(_size.width/2,_size.height/2)
self:createUI()
self:openTouch()
self:guide(_size)
end
function MainScene:createUI()
local _size = cc.Director:getInstance():getWinSize()
local function btnClicked(sender,_type)
print('--btn click--')
end
local btn = ccui.Button:create('res/DS07.png','res/DS08.png','res/DS09.png')
self:addChild(btn)
btn:setPosition(_size.width/3,_size.height/3)
btn:addTouchEventListener(btnClicked)
local function btnClicked2(sender,_type)
print('--btn2 click--')
end
local btn2 = ccui.Button:create('res/DS10.png','res/DS11.png','res/DS12.png')
self:addChild(btn2)
btn2:setPosition(_size.width/3,_size.height/4.5)
btn2:addTouchEventListener(btnClicked2)
_data['1'].normal = 'res/DS07.png'
_data['1'].pressed = 'res/DS08.png'
_data['1'].disabled = 'res/DS09.png'
_data['1'].event = btnClicked
_data['2'].normal = 'res/DS10.png'
_data['2'].pressed = 'res/DS11.png'
_data['2'].disabled = 'res/DS12.png'
_data['2'].event = btnClicked2
end
function MainScene:openTouch()
local listener = cc.EventListenerTouchOneByOne:create()
local function onTouchBegan( touch,event )
print("--touchBegan--")
return true
end
local function onTouchMoved( touch,event )
print("--touchMoved--")
end
local function onTouchEnded( touch,event )
print("--touchEnded--")
end
listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)
listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)
listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)
self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener,self)
local function onNodeEvent(event)
if event == 'exit' then
self:getEventDispatcher():removeEventListener(listener)
end
end
self:registerScriptHandler(onNodeEvent)
end
function MainScene:guide(_size)
local guideLayer = require('app/views/GuideShade').new(_size.width/3,_size.height/3,_data)
self:addChild(guideLayer)
end
return MainScene
_data中保存了2个按钮的数据,那么在创建GuideShade时传入这个数据,在开始结束都点在圆内时响应按钮的事件。
local function onTouchEnded( touch,event )
local pos = touch:getLocation()
if contain and self:checkPointInCircle(pos.x,pos.y) then
print('--in circle--')
local func = self._data['1'].event
if func then
func()
end
end
end
点圆可以看到
输出了–btn click–,原来点击Auto会输出2个–btn click–是因为,在回调函数中没有对事件进行判断,在点击到按钮的began和ended时都调用了回调函数,所以输出有2个,如果点到按钮之后moved会输出更多。而且回调函数有2个参数。在点击圆之后只是调用了这个回调函数,只输出了一次,并且没有2个参数,无法获得按钮以及事件,sender参数是可以通过_data记录并在调用回调函数时使用,在_data中添加一个sender,
local _data = {['1'] = {normal = nil,pressed = nil,disabled = nil,event = nil,name = nil,sender = nil},
['2'] = {normal = nil,pressed = nil,disabled = nil,event = nil,name = nil,sender = nil}}
_data['1'].sender = btn
print('--in circle--')
local func = self._data['1'].event
local sender = self._data['1'].sender
if func then
func(sender)
end
这里用的是一个圆形的模板,那么就有一个体验不好的地方,如果点击的点在圆内,但是在按钮外边,仍然能响应事件,这里需要做优化。另外,原本点击按钮时,有按钮的点击效果,现在没有这个按下去的效果,因为根本不是真正的点中了按钮,不过在_data中记录有这个按钮的资源,可以再创建一个按钮摆放在那个位置。
如果_data中记录有按钮本身,那么可以有个更好的办法,在GuideShade中,
function GuideShade:createUI()
local stencil = cc.Sprite:create('res/mask.png')
self.r = stencil:getContentSize().width / 2
local clipping = cc.ClippingNode:create(stencil)
clipping:setInverted(true)
clipping:setAlphaThreshold(0.3)
self:addChild(clipping,-1)
clipping:setPosition(self.x,self.y)
local pb = require("app/views/Pingbilayer").new()
clipping:addChild(pb)
pb:setPosition(-self.x,-self.y)
local sender = self._data['1'].sender
local btn = sender:clone()
self:addChild(btn)
btn:setPosition(self.x,self.y)
end
createUI方法中直接clone了按钮并摆放在原本按钮上方,这个clone出的按钮和原来的按钮用同一个回调函数,并且2个参数都能获取
这样不仅有按钮的点击效果,有按下去的感觉,而且在回调函数中能获取按钮和事件2个参数。不过这样也有一个缺点,就是看起来整个按钮都在圆形的上方,视觉效果不好。在实际开发中,还需要根据需求选择合适的方案。