Quick-Cocos2d-x-3.2中示例Coinfilp解析(二)

一、自定义按钮 BubbleButton
上一篇链接:http://www.cocoachina.com/bbs/read.php?tid=233838
上一篇我们讲到 气泡按钮 BubbleButton 的传入参数是:{ imgae = XXX, sound = XXX, prepare = XXX, listener = XXX },大家努力回想一下,使用过程中,
image是一张图片,
sound是一个音频文件,
prepare和listener都是一个方法。
使用代码(MeunScene.lua中的):

-- 3、“更过游戏”按钮
    self.moreGamesButton = BubbleButton.new({
            image = "#MenuSceneMoreGamesButton.png",
            sound = GAME_SFX.tapButton,
            prepare = function()
                audio.playSound(GAME_SFX.tapButton)
                self.startButton:setButtonEnabled(false)
            end,
            listener = function()
                app:enterMoreGamesScene()
            end,
        })
        :align(display.CENTER, display.left + 150, display.bottom + 300)
        :addTo(self)

```

现在我们来正式了解下这个自定义的按钮。
打开src\app\views\BubbleButton.lua, 看看代码,这个就不贴代码,贴的太多就磨灭大家的看帖动力了。还贴图更有动力,大家也用play3多看看效果,感觉更爽
  
粗略一看之后是不是觉得代码很多很乱很烦啊?其中仔细看看就会发现这个脚本里面只有一个方法,就是 new(param)
而其作用很明显:对参入的参数进行捕获,创建一个PushButton, 并封装进动作效果
整个方法内代码很多,头一次看会很乱,我先*
隐藏一部分代码*(为了方便理解,我改动了一点点顺序,但对程序没有丝毫影响)
大家就会发现该方法其实只干了4件事情,如下:
function BubbleButton.new(params)
    -- 1、创建PushButton,只设置了normal情况下图片(既是传入参数中的image的值)
    local button =  cc.ui.UIPushButton.new({normal = params.image}) 
   
    -- 2、将传入的回调函数先用listener变量保存起来,    
    local listener = params.listener
    
    -- 3、重新定义了传入参数中 params.listener的执行内容
    params.listener = function(tag)    
    
    -- 4、设置按钮点击响应事件
    button:onButtonClicked(function(tag)
    
    -- 返回该按钮
    return button
end

```


1、    创建按钮:没什么好讲的,大家这么聪明肯定都懂得。但是还是建议大家多看看Quick的框架源码,里面的有中文注释,很好理解,对学习Quick帮助极大,路径就在Quick-Cocos2d-x v3.2-RC0所在目录的quick-cocos2d-x-3.2rc0\quick\framework 里。

2、    捕获传入参数中的listener元素:由本地变量listener保存起来(我好讨厌名字一样啊,有时老不小心就看错!)。    为什么要找个本地变量来存储呢?等下你就知道了

3、    重新定义传入参数中的元素listener:有人也许会问:要重新定义,原来传入的有什么意思呢? 往上看看第2点,现在知道为什么要本地变量listener保存了吧。   现在我们把第3点的代码展开,看看里面做些什么事情
--  3、重新定义了参数表中 params.listener的执行内容
    params.listener = function(tag)        
        -- (1)如果参数中 prepare的值不为空,则先执行params.prepare()
        if params.prepare then
            params.prepare()
        end

        -- (2)zoom1和zoom2是两个效果函数,主要是move和scale两个动作,
        local function zoom1(offset, time, onComplete)
            local x, y = button:getPosition()
            local size = button:getContentSize()
            size.width = 200
            size.height = 200

            local scaleX = button:getScaleX() * (size.width + offset) / size.width
            local scaleY = button:getScaleY() * (size.height - offset) / size.height

            transition.moveTo(button, {y = y - offset, time = time})
            transition.scaleTo(button, {
                scaleX     = scaleX,
                scaleY     = scaleY,
                time       = time,
                onComplete = onComplete,
            })
        end

        local function zoom2(offset, time, onComplete)
            local x, y = button:getPosition()
            local size = button:getContentSize()
            size.width = 200
            size.height = 200

            transition.moveTo(button, {y = y + offset, time = time / 2})
            transition.scaleTo(button, {
                scaleX     = 1.0,
                scaleY     = 1.0,
                time       = time,
                onComplete = onComplete,
            })
        end

    --  (3)动作效果方法的组合使用
        --  设置按钮的点击功能无效,防止在还没做完动作又被玩家点上该按钮
        button:setButtonEnabled(false)

        -- 执行动作效果,    一系列的缩放效果之后,再开启按钮功能,最后执行之前的回调函数,这样一个动画按钮就新鲜出炉了。
        zoom1(40, 0.08, function()
            zoom2(40, 0.09, function()
                zoom1(20, 0.10, function()
                    zoom2(20, 0.11, function()
                        button:setButtonEnabled(true)   --  动作效果结束后,开启按钮的点击功能
                        listener(tag)                                    --  执行原先的params.listener的方法
                    end)
                end)
            end)
        end)
                
    end


```

重新定义了的param,listener 还是一个方法,该方法里的代码可以分成三部分:
 
(1)执行param.prepare这个元素所指向的方法;
           if params.prepare then
                   params.prepare()
           end
          该部分用了if判断语句,如果params.prepare 为空则不会执行,所以根据实际情况,传入参数中的元素prepare并不是一个必填项。

 
(2)定义了两个本地的效果方法zoom1和zoom2;
           zoom1和zoom2是两个效果函数,主要是move和scale两个动作,这个两个内容不难,我就不讲解了,大家看看理解下就好

 
(3)动作效果方法的组合使用;
          使用zoom1和zoom2进行组合使用,达到气泡效果的动作。难度不大但需要注意几点:
          A、使用回调函数,使动作连续
          B、执行动作效果前,先关闭该PushButton的按钮功能,防止在还没做完动作又被玩家点上该按钮:button:setButtonEnabled(false) , 效果结束了后在开启它的按钮功能
          C、在动作效果结束时,还在要执行之前我们用本来变量listener保存下来的方法,就是这个句代码:listener(tag),因为动作效果是我们附加的视觉效果,listener的方法才我们真正效果,不要忘本了哦


4、    设置按钮点击响应事件:代码展开如下
button:onButtonClicked(function(tag)
        params.listener(tag)
end)

```

是不是很简单,就是执行了我们刚才重新定义了的params.listener,
params.listener已经把所有需要做的事都做了:执行params.prepare、动作效果、还有最初的params.listener所定义的方法(如今在本地变量listener里)


现在再重新梳理一下,传入参数{ imgae = XXX, sound = XXX, prepare = XXX, listener = XXX }
image      是用于创建PushButton的图片,必须
sound      在BubbleButton.lua并没用到,但它其实是按钮被点击时的效果声音,等下再解释
prepare   是一个必定会被执行到的方法,但可以置空,大家就根据实际情况决定要不要写,应该写些什么。根据它的的名字,我理解这个元素是按钮被点击前需要做的事。
listener   同样是一个必定被执行的方法
再回到MenuScen.lua的ctor()的方法中,找到使用了BubbleButton的代码
--“开始”按钮
    self.startButton = BubbleButton.new({
            image = "#MenuSceneStartButton.png",
            sound = GAME_SFX.tapButton,
            prepare = function()
                audio.playSound(GAME_SFX.tapButton)  -- 播放特效声
                --self.startButton:setButtonEnabled(false)  
                self.moreGamesButton:setButtonEnabled(false) -- 先关闭功能,这样防止还没做完动作又被玩家点上别的按钮上了
            end,
            listener = function()
                app:enterChooseLevelScene()  -- 该方法在MyApp.lua中
            end,
        })
        :align(display.CENTER, display.right - 150, display.bottom + 300) -- 设置锚点,X坐标,Y坐标
        :addTo(self) -- 添加到该场景中

```

注释应该很清楚了,最后我想提出一下自己的改动:
看上面的代码,是不是有一句与原代码不同?就是注释掉了self.startButton:setButtonEnabled(false) ,并添加了 self.moreGamesButton:setButtonEnabled(false) ,  请让我解释一下, 原先的 self.startButton:setButtonEnabled(false) 这句代码的作用关闭startButton这个按钮的点击,防止这个按钮在还没做完动作又被玩家点上,但是看过BubbleButton.lua里面的代码后发现关闭按钮功能的代码已经内嵌在其中了,这样就发生了代码功能重复,所以我认为 self.startButton:setButtonEnabled(false) 这句代码是多余。
而添加了self.moreGamesButton:setButtonEnabled(false) 这句目的很简单,因为在MenuScene的场景一共就两个由BubbleButton 创建出来的按钮:startButton和moreGamesButton, 我希望在点击其中一个按钮时,另外的一个按钮的点击功能会被禁用,用户体验才更好(同时在另外一个按钮代码也应该做相应的修改)

以上只是我个人的理解,不知道对不对,大家多担当,多给建议
同时,对于这个二次封装的按钮,大家也可以看看这位大神讲解http://cn.cocos2d-x.org/tutorial/show?id=1350


二、进入MoreGamesScene场景
完全了解MenuScene这个场景后,我们随着startButton和moreGamesButton这两个按钮进入其他场景,继续学习吧
首先,点击moreGamesButton,我们将进入MoreGamesScene场景,如下图:
 
真是单调的界面啊,我们还是打开src\app\scenes\MoreGamesScene.iua来直接看代码吧:
-- 导入AdBar.lua
local AdBar = import("..views.AdBar")

-- 创建一个名为MoreGamesScene的场景类
local MoreGamesScene = class("MoreGamesScene", function()
    return display.newScene("MoreGamesScene")
end)

function MoreGamesScene:ctor()
    -- 1、背景
    self.bg = display.newSprite("#MenuSceneBg.png", display.cx, display.cy)
    self:addChild(self.bg)
    
    --2、信息条
    self.adBar = AdBar.new()
    self:addChild(self.adBar)

    --3、后退按钮
    cc.ui.UIPushButton.new("#BackButton.png")
        :align(display.CENTER, display.right - 100, display.bottom + 120)
         -- 添加点击的响应事件
        :onButtonClicked(function()
            app:enterMenuScene()  -- 回到MenuScene中
        end)
        :addTo(self)
end

return MoreGamesScene

```
 

1、背景:创建一精灵,并添加到场景中

2、信息条:在上一篇中已经大胆预测它会再出现了,果不其然啊,不到多少,大家自己看代码吧

3、后退按钮:使用是Quick框架封装好的PushButton按钮创建,源码在quick-3.2rc0-win\quick-cocos2d-x-3.2rc0\quick\framework\cc\ui,大家要养成多查看框架代码的习惯,用法里面讲得很清楚
这个场景太简单了,没什么好讲,我们还是默默的按后退键吧

:13: 顶一个,写的好

谢谢 :2:

楼主学习很用心,继续支持!:2:

顶顶顶,支持

楼主你好,请教一个问题,楼主把两个按钮中的prepare函数内容改成禁用另一个按钮,没有出错嘛?我这里按照楼主这样改之后,运行点击按钮后直接崩溃,IDE控制台下没有任何错误信息,是直接程序崩溃了。改回原来的就没有问题了,不知道是哪里出错了啊

提问提问,按照作者所说的

“看上面的代码,是不是有一句与原代码不同?就是注释掉了self.startButton:setButtonEnabled(false) ,并添加了 self.moreGamesButton:setButtonEnabled(false) , 请让我解释一下, 原先的 self.startButton:setButtonEnabled(false) 这句代码的作用关闭startButton这个按钮的点击,防止这个按钮在还没做完动作又被玩家点上,但是看过BubbleButton.lua里面的代码后发现关闭按钮功能的代码已经内嵌在其中了,这样就发生了代码功能重复,所以我认为 self.startButton:setButtonEnabled(false) 这句代码是多余。”

我也按照作者所说的做了,但是得到的结果是一按按钮程序就崩溃关闭,不知道什么原因,求大神解答

反正经过多次修改代码测试,感觉就是一个事件当中,不能同时禁用多个按钮功能,否则程序就崩溃了

还有一个比较奇怪的问题,这两个按钮,如果只创建一个,点击必定程序崩溃,不知道错误在哪里。创建两个就是正常的。。。求解释,完全不知道在哪里出错,断点进去看也没找出问题,在某一步直接程序崩溃