在cocos2d-x游戏中实现tips功能

前端之家收集整理的这篇文章主要介绍了在cocos2d-x游戏中实现tips功能前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

原文请猛戳:
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左侧,tipstarget_node有一个固定的水平间距(我们不妨定义为DEFAULT_TIPS_DIST);tipstarget_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.getPositionOfNodeMyPackage.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是否有显示,这需要回溯看父节点的visibilitygetCascadeVisibility定义如下:

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

猜你在找的Cocos2d-x相关文章