原文请猛戳:
http://galoisplusplus.coding.me/blog/2016/01/16/tips-in-cocos2d-x-game/
这次分享一个简单的小功能,用cocos2d-x实现tips效果,作为之前一篇博文的后续。tips的行为很简单:点击某个node(我们不妨称它为target_node
)触发,当点击区域在target_node
范围时出现tips
,否则隐藏tips
(有些情况需要指定有效点击范围不在某些node中,我们把这些node称为exclude_nodes
);当target_node
位于屏幕左半边时,tips
出现在target_node
右侧;否则tips
就出现在target_node
左侧,tips
和target_node
有一个固定的水平间距(我们不妨定义为DEFAULT_TIPS_disT
);tips
和target_node
底部对齐,但tips
不能超过屏幕范围。
不废话,先上代码:
function setupTips(params) local targetNode = params.target_node local tips = params.tips local excludeNodes = params.exclude_nodes or {} local DEFAULT_TIPS_disT = 10 local TIPS_ZORDER = 1000 if tolua.isnull(targetNode) or tolua.isnull(tips) then return end tips:setVisible(false) targetNode:setTouchEnabled(false) display.getRunningScene():addChild(tips,TIPS_ZORDER) targetNode:addNodeEventListener(cc.NODE_EVENT,function(event) if event.name == "exit" then local scene = display.getRunningScene() scene:performWithDelay(MyPackage.callbackWrapper({scene},function() if not tolua.isnull(tips) then tips:setVisible(false) end end),0) local eventdispatcher = display.getRunningScene():getEventdispatcher() eventdispatcher:removeEventListenersForTarget(targetNode) end end) ------------------------------------------------------------ local function setTipsPosition() local leftBottomPos = MyPackage.getPositionOfNode(targetNode,display.LEFT_BottOM) local targetNodePos = display.getRunningScene():convertToNodeSpace(targetNode:getParent():convertToWorldspace(leftBottomPos)) local targetNodeAnchorPoint = targetNode:getAnchorPoint() local tipsPos = targetNodePos local tipsAnchorPoint = cc.p(0,0) local director = cc.Director:getInstance() local glView = director:getopenGLView() local frameSize = glView:getFrameSize() local viewSize = director:getVisibleSize() if targetNodePos.x <= frameSize.width * 0.5 / glView:getScaleX() then -- show tips on the right of the targetNode if the targetNode is on the left screen tipsPos.x = tipsPos.x + targetNode:getContentSize().width + DEFAULT_TIPS_disT else -- show tips on the left of the targetNode otherwises tipsPos.x = tipsPos.x - DEFAULT_TIPS_disT tipsAnchorPoint.x = 1 end if targetNodePos.y + tips:getContentSize().height > viewSize.height then tipsPos.y = viewSize.height tipsAnchorPoint.y = 1 end if targetNodePos.y < 0 then targetNodePos.y = 0 end tips:ignoreAnchorPointForPosition(false) tips:setAnchorPoint(tipsAnchorPoint) tips:setPosition(tipsPos) end ------------------------------------------------------------ local function activeFunc() local scene = display.getRunningScene() -- NOTE: delay util the next frame in order to get the correct Worldspace position scene:performWithDelay(MyPackage.callbackWrapper({scene,tips},function() setTipsPosition() tips:setVisible(true) end),0) end local function inactiveFunc() if not tolua.isnull(tips) then tips:setVisible(false) end end local function isTouchInNode(touch,node) if tolua.isnull(node) or tolua.isnull(touch) then return false end local localLocation = node:convertToNodeSpace(touch:getLocation()) local width = node:getContentSize().width local height = node:getContentSize().height local rect = cc.rect(0,width,height) return getCascadeVisibility(node) and node:isRunning() and cc.rectContainsPoint(rect,localLocation) end local function isActive(touch) local isExcluded = false for _,excludeNode in ipairs(excludeNodes) do isExcluded = isExcluded or isTouchInNode(touch,excludeNode) end return isTouchInNode(touch,targetNode) and not isExcluded end local function onTouchBegan(touch,event) if isActive(touch) then activeFunc() return true else return false end end local function onTouchMoved(touch,event) local scene = display.getRunningScene() scene:performWithDelay(MyPackage.callbackWrapper({scene},function() if isActive(touch) then activeFunc() else inactiveFunc() end end),0) end local function onTouchEnded(touch,function() inactiveFunc() end),0) end local listener = cc.EventListenerTouchOneByOne:create() listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN) listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED) listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED) listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_CANCELLED) local eventdispatcher = display.getRunningScene():getEventdispatcher() eventdispatcher:removeEventListenersForTarget(targetNode) eventdispatcher:addEventListenerWithSceneGraPHPriority(listener,targetNode) end
关于MyPackage.getPositionOfNode
、MyPackage.callbackWrapper
等helper functions请参见之前某篇博文。
上面的代码基本是很简单的,除了有几点需要额外说明一下:
1.几处调用到performWithDelay
的地方。这是因为我们用target_node
的Worldspace坐标来确定tips
的位置,当target_node
位于某个可滚动的node(如ScrollView
)中时,需要延迟到下一帧才能拿到它正确的Worldspace坐标,所以我们用了quickx定义的Node:performWithDelay
来做延时。之所以用这个函数而不用scheduler,是因为它在Node的生命周期中,我们不需要担心如何去安全销毁scheduler所产生的handler。事实上我们只要看一下quickx定义在NodeEx.lua中的Node:performWithDelay
就一目了然了:
function Node:performWithDelay(callback,delay) local action = transition.sequence({ cc.DelayTime:create(delay),cc.CallFunc:create(callback),}) self:runAction(action) return action end
2.我们指定target_node
在收到exit
事件时隐藏tips
,这是因为target_node
可能在某些ClippingNode
(如ScrollView
)中,当它超出区域不再显示时,tips
也不应该被显示。
3.当同一个位置有多个具有tips
行为的target_node
时,需要判断当前的target_node
是否有显示,这需要回溯看父节点的visibility
,getCascadeVisibility
定义如下:
function getCascadeVisibility(node) if tolua.isnull(node) then return true end local visibility = node:isVisible() if visibility then local parent = node:getParent() visibility = visibility and getCascadeVisibility(parent) end return visibility end
原文地址:https://www.jb51.cc/cocos2dx/340298.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。