Quick-cocos2d-x-3.2中示例Coinfilp解析(五)

一、最后的前言
依旧使用久方法:顺藤摸瓜,一点一点发现,打开ChooseLevelScene.lua吧

local AdBar = import("..views.AdBar")
local LevelsList = import("..views.LevelsList")

local ChooseLevelScene = class("ChooseLevelScene", function()
    return display.newScene("ChooseLevelScene")
end)

function ChooseLevelScene:ctor()
    -- 背景
    local bg = display.newSprite("#OtherSceneBg.png")
    -- make background sprite always align top 用于对齐顶部
    bg:setPosition(display.cx, display.top - bg:getContentSize().height / 2)
    self:addChild(bg)
    
    -- 标题
    local title = display.newSprite("#Title.png", display.cx, display.top - 100)
    self:addChild(title)
    
    -- 信息条
    local adBar = AdBar.new()
    self:addChild(adBar)

    -- create levels list 创建等级列表
    local rect = cc.rect(display.left, display.bottom + 180, display.width, display.height - 280)
    self.levelsList = LevelsList.new(rect)
    self.levelsList:addEventListener("onTapLevelIcon", handler(self, self.onTapLevelIcon))
    self:addChild(self.levelsList)

    -- 后退按钮
    cc.ui.UIPushButton.new({normal = "#BackButton.png", pressed = "#BackButtonSelected.png"})
        :align(display.CENTER, display.right - 100, display.bottom + 120)
        :onButtonClicked(function()
            app:enterMenuScene()
        end)
        :addTo(self)
end

function ChooseLevelScene:onTapLevelIcon(event)
    audio.playSound(GAME_SFX.tapButton)
    app:playLevel(event.levelIndex)
end

function ChooseLevelScene:onEnter()
    self.levelsList:setTouchEnabled(true)
end

return ChooseLevelScene


```

不难,唯一需要继续深入的就是这段代码:
-- create levels list 创建等级列表
    local rect = cc.rect(display.left, display.bottom + 180, display.width, display.height - 280)
    self.levelsList = LevelsList.new(rect)
    self.levelsList:addEventListener("onTapLevelIcon", handler(self, self.onTapLevelIcon))
self:addChild(self.levelsList)


```


        其实,想要顺着代码往看下,我们将会把要这个游戏剩下的五个脚本关联起立:
    LevelsList.lua
    LevelsListCell.lua
    PageControl.lua
    ScrollView.lua
    ScrollViewCell.lua
只要明白了这五个脚本,就懂得了该游戏的关卡选择场景是如何建立的,进而整个游戏也就完整了解了一边。先让大家了解下我对这最后的五个脚本理解的关系:

          LevelsList           继承自→        PageControl       继承自→     ScrollView
              LevelsListCell    继承自→        ScrollViewCell
             LevelsList            在内部使用了        LevelsListCell

        让我们先从简单的讲起,就是cell(单元),先明白,该游戏运用时,一个页面就是一个cell(单元),如下图:
         
包含了1到16有一个看不见的矩形,它就一个cell(单元),你向左滑动一下就会进入下一个cell(单元):17到32



二、cell(单元) 
        从ScrollViewCell.lua开始:
local ScrollViewCell = class("ScrollViewCell", function(contentSize)
    local node = display.newNode() -- 该类基础只是一个节点
    if contentSize then node:setContentSize(contentSize) end    -- 根据传入参入确立该节点的ContentSize
    node:setNodeEventEnabled(true)  --设置该节点可以 为特定事件设置处理函数
    cc(node):addComponent("components.behavior.EventProtocol"):exportMethods()
    return node
end)

function ScrollViewCell:onTouch(event, x, y)
end

function ScrollViewCell:onTap(x, y)
end

-- 退出时调用
function ScrollViewCell:onExit()
    --移除所用的事件响应事件
    self:removeAllEventListeners()
end

return ScrollViewCell


```

ScrollViewCell.lua并不复杂,因为它只是作为一个“骨架”存在,很多功能并没有实现,所以需要我们再去扩展,所以有了LevelsListCell.lua,一定有人觉得为什么不直接写一个LevelsListCell.lua就够了呢?反正我们就扩展一个类而已,干嘛要“骨架”呢?  这我回答不好,只能说这样编程遵循了良好的设计模式,对以后程序的改良有很大的好处。
        回到正题,进入LevelsListCell.lua:
local ScrollViewCell = import("..ui.ScrollViewCell")

-- 继承自ScrollViewCell
local LevelsListCell = class("LevelsListCell", ScrollViewCell)


function LevelsListCell:ctor(size, beginLevelIndex, endLevelIndex, rows, cols)
    
    local rowHeight = math.floor((display.height - 340) / rows)   -- 每行的高度
    local colWidth = math.floor(display.width * 0.9 / cols)       -- 每列的宽度
    
    -- 使用了批量渲染对象,在Board.lua也用过
    local batch = display.newBatchNode(GAME_TEXTURE_IMAGE_FILENAME)
    self:addChild(batch)
    self.pageIndex = pageIndex
    -- button集合
    self.buttons = {}

    -- 第一个将要加入的button的X,Y坐标
    local startX = (display.width - colWidth * (cols - 1)) / 2
    local y = display.top - 220
    
    -- 捕获传入参数beginLevelIndex
    local levelIndex = beginLevelIndex

    -- 添加button,每个cell(单元)是16个按钮哦
    for row = 1, rows do
        local x = startX
        for column = 1, cols do
            -- button的一系列处理
            local icon = display.newSprite("#LockedLevelIcon.png", x, y)
            batch:addChild(icon)
            icon.levelIndex = levelIndex
            self.buttons#self.buttons + 1] = icon 
            
            -- 等级标签
            local label = cc.ui.UILabel.new({
                UILabelType = 1,
                text  = tostring(levelIndex),
                font  = "UIFont.fnt"
            })
            :align(cc.ui.TEXT_ALIGN_CENTER, x, y - 4)
            self:addChild(label)
            
            -- 处理完一个button后,重置数据,为添加下一个button做准备           
            x = x + colWidth
            levelIndex = levelIndex + 1
            if levelIndex > endLevelIndex then break end
        end

        y = y - rowHeight
        if levelIndex > endLevelIndex then break end
    end

    -- add highlight level icon
    self.highlightButton = display.newSprite("#HighlightLevelIcon.png")
    self.highlightButton:setVisible(false)
    self:addChild(self.highlightButton,100)
end

-- 触摸响应函数
function LevelsListCell:onTouch(event, x, y)
    if event == "began" then
        local button = self:checkButton(x, y)
        if button then
            self.highlightButton:setVisible(true)
            self.highlightButton:setPosition(button:getPosition())
        end
    elseif event ~= "moved" then
        self.highlightButton:setVisible(false)
    end
end

-- 触摸敲中button的响应函数
function LevelsListCell:onTap(x, y)
    local button = self:checkButton(x, y)
    if button then
        self:dispatchEvent({name = "onTapLevelIcon", levelIndex = button.levelIndex})
    end
end

-- 获取传入参数X,Y坐标所对应的button
function LevelsListCell:checkButton(x, y)
    local pos = cc.p(x, y)
    -- 遍历所有的button
    for i = 1, #self.buttons do
        local button = self.buttons*
        -- 如果传入的点在该button的方位内,则返回该button
        if cc.rectContainsPoint(button:getBoundingBox(), pos) then
            return button
        end
    end
    return nil
end

return LevelsListCell

```

扩展后的Cell,在ctor中对Cell的视觉界面进行了排版和布局,同时还有两个响应函数:onTouch(event, x, y)和onTap(x, y)
        这里我要特别解释一句代码: self:dispatchEvent({name = "onTapLevelIcon", levelIndex = button.levelIndex}) 因为我研究了很久(因为百度不到解释,源码又看不太懂T.T),       意思就委派一个Event,其中name属性是必须,作为事件的名字,但要触发该事件时就需要用到,而其他属性可以任意。这句代码结束后就相当LevelsListCell 这个类有了一个Event,可以看做一个表event={{name = "onTapLevelIcon", levelIndex = button.levelIndex}}



三、滚动控件ScrollView.lua
local ScrollView = class("ScrollView", 
-- 创建一个控件基础的节点
function(rect)
    if not rect then rect = cc.rect(0, 0, 0, 0) end
    local node = display.newClippingRegionNode(rect)  -- 剪裁区域的节点
    node:setNodeEventEnabled(true)
    cc(node):addComponent("components.behavior.EventProtocol"):exportMethods()
    return node
end)

ScrollView.DIRECTION_VERTICAL   = 1    -- 垂直方向
ScrollView.DIRECTION_HORIZONTAL = 2    -- 水平方向


-- 参数:区域,拖动方向
function ScrollView:ctor(rect, direction)
    assert(direction == ScrollView.DIRECTION_VERTICAL or direction == ScrollView.DIRECTION_HORIZONTAL,
           "ScrollView:ctor() - invalid direction")

    self.dragThreshold = 40                 -- 拖动最小临界值
    self.bouncThreshold = 140               -- 拖动到能翻页的最小临界值   
    self.defaultAnimateTime = 0.4           -- 默认动画时间
    self.defaultAnimateEasing = "backOut"   -- 默认的ease

    self.direction = direction    -- 滚动方向
    self.touchRect = rect         -- 触摸矩形
    self.offsetX = 0              -- X轴偏移量
    self.offsetY = 0              -- Y轴偏移量
    self.cells = {}               -- 单元集合
    self.currentIndex = 0         -- 当前索引(对应self.cells)

    -- 为self的节点事件cc.NODE_EVENT 设置处理函数(返回一个id表示注册成功)
    self:addNodeEventListener(cc.NODE_EVENT, function(event)
        if event.name == "enter" then
            self:onEnter()
        elseif event.name == "exit" then
            self:onExit()
        end
    end)

    -- create container layer  创建一个容器层
    self.view = display.newLayer()
    self:addChild(self.view)   
    -- 为self.view的触摸事件cc.NODE_TOUCH_EVENT 设置处理函数
    self.view:addNodeEventListener(cc.NODE_TOUCH_EVENT, function(event)
        return self:onTouch(event.name, event.x, event.y)
    end)
end

---- 一些简单的对外接口函数

-- 得到当前索引所指向的单元
function ScrollView:getCurrentCell()
    if self.currentIndex > 0 then
        return self.cells
    else
        return nil
    end
end

-- 得到当前索引
function ScrollView:getCurrentIndex()
    return self.currentIndex
end

-- 设置当前索引
function ScrollView:setCurrentIndex(index)
    self:scrollToCell(index)
end

-- 添加单元
function ScrollView:addCell(cell)
    self.view:addChild(cell)
    self.cells#self.cells + 1] = cell
    self:reorderAllCells()
    self:dispatchEvent({name = "addCell", count = #self.cells})
end

-- 根据索引插入一个单元
function ScrollView:insertCellAtIndex(cell, index)
    self.view:addChild(cell)
    table.insert(self.cells, index, cell)
    self:reorderAllCells()
    self:dispatchEvent({name = "insertCellAtIndex", index = index, count = #self.cells})
end

-- 根据索引移除一个单元
function ScrollView:removeCellAtIndex(index)
    local cell = self.cells
    cell:removeSelf()
    table.remove(self.cells, index)
    self:reorderAllCells()
    self:dispatchEvent({name = "removeCellAtIndex", index = index, count = #self.cells})
end

-- 得到容器层
function ScrollView:getView()
    return self.view
end

-- 得到可触摸矩形
function ScrollView:getTouchRect()
    return self.touchRect
end

-- 设置可触摸矩形
function ScrollView:setTouchRect(rect)
    self.touchRect = rect
    self:dispatchEvent({name = "setTouchRect", rect = rect})
end

-- 得到剪裁后的矩形
function ScrollView:getClippingRect()
    return self:getClippingRegion()
end

-- 设置剪裁后的矩形
function ScrollView:setClippingRect(rect)
    self:setClippingRegion(rect)
    -- 委派事件 ,名字:setClippingRect
    self:dispatchEvent({name = "setClippingRect", rect = rect})
end

-- 滚动cell(单元)  传入参数,index当前页码, 后面三个为跳转Cell时执行动作的参数
function ScrollView:scrollToCell(index, animated, time, easing)
    -- cell(单元)总数
    local count = #self.cells
    if count < 1 then
        self.currentIndex = 0
        return
    end
    
    -- 对index进行判断与处理
    if index < 1 then
        index = 1
    elseif index > count then
        index = count
    end
    self.currentIndex = index
    
    -- 根据滚动方向,设置偏移量
    local offset = 0
    for i = 2, index do
        local cell = self.cells*
        local size = cell:getContentSize()
        if self.direction == ScrollView.DIRECTION_HORIZONTAL then
            offset = offset - size.width
        else
            offset = offset + size.height
        end
    end

    self:setContentOffset(offset, animated, time, easing)
    -- 委派事件
    self:dispatchEvent({name = "scrollToCell", animated = animated, time = time, easing = easing})
end

-- 检测容器层self.view是否允许触摸
function ScrollView:isTouchEnabled()
    return self.view:isTouchEnabled()
end

-- 设置容器层是否允许触摸响应(默认是false)
function ScrollView:setTouchEnabled(enabled)
    self.view:setTouchEnabled(enabled)
    -- self:setTouchEnabled(enabled)
    self:dispatchEvent({name = "setTouchEnabled", enabled = enabled})
end


---- events 事件处理函数

-- Began
function ScrollView:onTouchBegan(x, y)
    self.drag = {
        currentOffsetX = self.offsetX,
        currentOffsetY = self.offsetY,
        startX = x,
        startY = y,
        isTap = true,
    }

    local cell = self:getCurrentCell()
    cell:onTouch(event, x, y)

    return true
end

-- Moved
function ScrollView:onTouchMoved(x, y)
    local cell = self:getCurrentCell()
    if self.direction == ScrollView.DIRECTION_HORIZONTAL then
        if self.drag.isTap and math.abs(x - self.drag.startX) >= self.dragThreshold then
            self.drag.isTap = false
            cell:onTouch("cancelled", x, y)
        end

        if not self.drag.isTap then
            self:setContentOffset(x - self.drag.startX + self.drag.currentOffsetX)
        else
            cell:onTouch(event, x, y)
        end
    else
        if self.drag.isTap and math.abs(y - self.drag.startY) >= self.dragThreshold then
            self.drag.isTap = false
            cell:onTouch("cancelled", x, y)
        end

        if not self.drag.isTap then
            self:setContentOffset(y - self.drag.startY + self.drag.currentOffsetY)
        else
            cell:onTouch(event, x, y)
        end
    end
end

-- Ended  里面调用了onTouchCancelled(x, y)与onTouchEndedWithTap(x, y)
function ScrollView:onTouchEnded(x, y)
    if self.drag.isTap then
        self:onTouchEndedWithTap(x, y)
    else
        self:onTouchEndedWithoutTap(x, y)
    end
    self.drag = nil
end

-- Cancelled  
function ScrollView:onTouchCancelled(x, y)
    self.drag = nil
end

-- 触摸结束时,结束点敲中按钮
function ScrollView:onTouchEndedWithTap(x, y)
    local cell = self:getCurrentCell()
    cell:onTouch(event, x, y)
    cell:onTap(x, y)      -- 该函数的启动将会触发许多函数!!                  
end

-- 触摸结束时,结束点没有敲中按钮
function ScrollView:onTouchEndedWithoutTap(x, y)
    error("ScrollView:onTouchEndedWithoutTap() - inherited class must override this method")
end



-- onTouch  根据事件调用 Began、Moved、Ended与Cancelled
function ScrollView:onTouch(event, x, y)
    if self.currentIndex < 1 then return end

    if event == "began" then
        -- 判断起始的触摸点是否在可触摸矩形中,若不在则直接返回不做任何操作
        if not cc.rectContainsPoint(self.touchRect, cc.p(x, y)) then return false end
        return self:onTouchBegan(x, y)
    elseif event == "moved" then
        self:onTouchMoved(x, y)
    elseif event == "ended" then
        self:onTouchEnded(x, y)
    else -- cancelled
        self:onTouchCancelled(x, y)
    end
end



---- private methods 私有方法

-- 重新整理所有的单元
function ScrollView:reorderAllCells()
    -- 1、设置每个Cell(单元)的position,注意cell的锚点在(0,0)
    local count = #self.cells
    local x, y = 0, 0
    local maxWidth, maxHeight = 0, 0
    for i = 1, count do
        local cell = self.cells*
        cell:setPosition(x, y)
        -- 根据滚动方向确定下个cell的position
        if self.direction == ScrollView.DIRECTION_HORIZONTAL then
            local width = cell:getContentSize().width
            if width > maxWidth then maxWidth = width end
            x = x + width
        else
            local height = cell:getContentSize().height
            if height > maxHeight then maxHeight = height end
            y = y - height
        end
    end
    
    -- 2、重置数据
    if count > 0 then
        if self.currentIndex < 1 then
            self.currentIndex = 1
        elseif self.currentIndex > count then
            self.currentIndex = count
        end
    else
        self.currentIndex = 0
    end

    -- 3、添加完所有Cell后,根据滚动方向,设置好容器层的大小
    local size
    if self.direction == ScrollView.DIRECTION_HORIZONTAL then
        size = cc.size(x, maxHeight)
    else
        size = cc.size(maxWidth, math.abs(y))
    end
    self.view:setContentSize(size)
end

-- 根据偏移量触发动作
function ScrollView:setContentOffset(offset, animated, time, easing)
    local ox, oy = self.offsetX, self.offsetY
    local x, y = ox, oy
    if self.direction == ScrollView.DIRECTION_HORIZONTAL then
        self.offsetX = offset
        x = offset

        local maxX = self.bouncThreshold
        local minX = -self.view:getContentSize().width - self.bouncThreshold + self.touchRect.width
        if x > maxX then
            x = maxX
        elseif x < minX then
            x = minX
        end
    else
        self.offsetY = offset
        y = offset

        local maxY = self.view:getContentSize().height + self.bouncThreshold - self.touchRect.height
        local minY = -self.bouncThreshold
        if y > maxY then
            y = maxY
        elseif y < minY then
            y = minY
        end
    end

    -- 根据传入的动作参数执行动作
    if animated then
        transition.stopTarget(self.view)
        transition.moveTo(self.view, {
            x = x,
            y = y,
            time = time or self.defaultAnimateTime,
            easing = easing or self.defaultAnimateEasing,
        })
    else
        -- 如果animated这项为空,就没有动作了,直接使用setPosition跳转到下一个Cell
        self.view:setPosition(x, y)
    end
end

-- onExit
function ScrollView:onExit()
    -- 移除所有指定类型的事件处理函数
    self:removeAllEventListeners()
end

return ScrollView

```

该脚本代码很多,可能比较麻烦看,但基本可以为四大部分:
    ctor,在里面设置了基本属性
    对外接口函数,大部分为对属性进行操作的简单接口函数
    events 事件处理函数
    private methods 私有方法
剩下的需要大家看着注释慢慢理解了,我担心越说越混乱

四、PageControl**
        PageControl继承自ScrollView,并且改动很小,只是重写一个方法,就是:onTouchEndedWithoutTap(x, y),原本该方法只在未点中时输出信息,但重写后会根据触摸偏移量进行翻页动作
local ScrollView = import(".ScrollView")

-- 继承自ScrollView
local PageControl = class("PageControl", ScrollView)

-- 重新定义了触摸点未敲中button时的函数,会根据触摸的偏移量执行翻页的动作
function PageControl:onTouchEndedWithoutTap(x, y)
    local offsetX, offsetY = self.offsetX, self.offsetY
    local index = 0
    local count = #self.cells
    if self.direction == ScrollView.DIRECTION_HORIZONTAL then
        offsetX = -offsetX
        local x = 0
        local i = 1
        while i <= count do
            local cell = self.cells*
            local size = cell:getContentSize()
            if offsetX < x + size.width / 2 then
                index = i
                break
            end
            x = x + size.width
            i = i + 1
        end
        if i > count then index = count end
    else
        local y = 0
        local i = 1
        while i <= count do
            local cell = self.cells*
            local size = cell:getContentSize()
            if offsetY < y + size.height / 2 then
                index = i
                break
            end
            y = y + size.height
            i = i + 1
        end
        if i > count then index = count end
    end

    self:scrollToCell(index, true)
end

return PageControl


```




五、最终的成品LevelsList**
        LevelsList继承自PageControl ,重写了两个方法:ctor和scrollToCell,与其说是重写不如说是为他们增加了功能,两个方法都首先执行了父类的方法,在ctor增加了按钮的布局添加与指示灯,在scrollToCell中增加了指示灯的动作效果。
        这就是指示灯:
         
        同时,扩增了一个方法onTapLevelIcon,作用是委派事件。
local LevelsListCell = import(".LevelsListCell")
local Levels = import("..data.Levels")

local PageControl = import("..ui.PageControl")

-- 继承自PageControl
local LevelsList = class("LevelsList", PageControl)

LevelsList.INDICATOR_MARGIN = 46 -- 指示灯的间隔

function LevelsList:ctor(rect)
    --先执行方法定义好的方法
    LevelsList.super.ctor(self, rect, PageControl.DIRECTION_HORIZONTAL)

    -- 1、add cells
    -- 默认是4行4列,横的是行,竖的是列
    local rows, cols = 4, 4
    -- 如果分辨率的高超过1000,而变成5行
    if display.height > 1000 then rows = rows + 1 end

    -- 总页数 :7页,也就是7个cell
    local numPages = math.ceil(Levels.numLevels() / (rows * cols))
    local levelIndex = 1

    -- 使用for循环添加7个cell
    for pageIndex = 1, numPages do
        -- 每个cell最后一个关卡的等级
        local endLevelIndex = levelIndex + (rows * cols) - 1
        -- 如果cell的最后一个关卡等级大于Levels.lua里设定好的100个等级话的,那么endLevelIndex就为100,也就是第7个cell
        if endLevelIndex > Levels.numLevels() then
            endLevelIndex = Levels.numLevels()
        end
        -- 创建cell
        local cell = LevelsListCell.new(cc.size(display.width, rect.height), levelIndex, endLevelIndex, rows, cols)
        cell:addEventListener("onTapLevelIcon", function(event) return self:onTapLevelIcon(event) end)
        self:addCell(cell)
        
        --重置数据,为下个循环添加cell做准备
        levelIndex = endLevelIndex + 1
    end


    -- 2、add indicators 添加指示灯
    local x = (self:getClippingRect().width - LevelsList.INDICATOR_MARGIN * (numPages - 1)) / 2
    local y = self:getClippingRect().y + 20

    self.indicator_ = display.newSprite("#LevelListsCellSelected.png")
    self.indicator_:setPosition(x, y)
    self.indicator_.firstX_ = x

    for pageIndex = 1, numPages do
        local icon = display.newSprite("#LevelListsCellIndicator.png")
        icon:setPosition(x, y)
        self:addChild(icon)
        x = x + LevelsList.INDICATOR_MARGIN
    end

    self:addChild(self.indicator_)
    
end

-- 重写scrollToCell方法,添加指示灯的动作效果
function LevelsList:scrollToCell(index, animated, time)
    --1、先执行方法定义好的方法
    LevelsList.super.scrollToCell(self, index, animated, time)

    --2、指示灯动作效果代码
    transition.stopTarget(self.indicator_)
    local x = self.indicator_.firstX_ + (self:getCurrentIndex() - 1) * LevelsList.INDICATOR_MARGIN
    if animated then
        time = time or self.defaultAnimateTime
        transition.moveTo(self.indicator_, {x = x, time = time / 2})
    else
        self.indicator_:setPositionX(x)
    end
end

-- 点中图标是委派一个事件
function LevelsList:onTapLevelIcon(event)
    self:dispatchEvent({name = "onTapLevelIcon", levelIndex = event.levelIndex})
end

return LevelsList


```




六、使用
        回到最初的代码:
local rect = cc.rect(display.left, display.bottom + 180, display.width, display.height - 280)
    self.levelsList = LevelsList.new(rect)
    self.levelsList:addEventListener("onTapLevelIcon", handler(self, self.onTapLevelIcon))
self:addChild(self.levelsList)


```

使用很简单了,只要传入一个矩形参数就足够了,其他的东西在LevelsListCell和LevelsList里都已经扩展好了。

        让我们看一下这句代码self.levelsList:addEventListener("onTapLevelIcon", handler(self, self.onTapLevelIcon))找到self.onTapLevelIcon:
function ChooseLevelScene:onTapLevelIcon(event)
          audio.playSound(GAME_SFX.tapButton)
          app:playLevel(event.levelIndex)
end


```

这个方法就把我们之前第四篇所讲的一切联系上了,进入游戏进行场景了!



七、再讲讲dispatchEvent
        先说清楚,这个地方完全了看着这个游戏代码自我理解,不太能讲得明白,大家可以参考这篇文章:http://www.cocoachina.com/bbs/read.php?tid=235255

        还是继续看这句代码:self.levelsList:addEventListener("onTapLevelIcon", handler(self, self.onTapLevelIcon))
        问题来了,事件"onTapLevelIcon"哪里来的呢?回到levelsList.lua发现:
-- 点中图标是委派一个事件
function LevelsList:onTapLevelIcon(event)
    self:dispatchEvent({name = "onTapLevelIcon", levelIndex = event.levelIndex})
end


```

并且在ctor中发现了使用它的代码:
cell:addEventListener("onTapLevelIcon", function(event) return self:onTapLevelIcon(event) end)


```

意思就是LevelsList里的onTapLevelIcon(event)函数一定执行,就会触发名为"onTapLevelIcon"的Event,同时返回一个参数event={ name = "onTapLevelIcon",levelIndex = event.levelIndex}}


        但在这里,我们又发现了Cell里也有一个"onTapLevelIcon"事件,好吧,乖乖回去LevelsListCell
-- 触摸敲中button的响应函数
function LevelsListCell:onTap(x, y)
    local button = self:checkButton(x, y)
    if button then
        self:dispatchEvent({name = "onTapLevelIcon", levelIndex = button.levelIndex})
    end
end


```

而这个onTap(x, y)是在何时被调用的呢?答案就在ScrollView:
-- 触摸结束时,结束点敲中按钮
function ScrollView:onTouchEndedWithTap(x, y)
    local cell = self:getCurrentCell()
    cell:onTouch(event, x, y)
    cell:onTap(x, y)      -- 该函数的启动将会触发许多函数!!                  
end


```


       
 讲得这么乱,总结起来就当触摸结束时,结束点敲中按钮,将会触发事件,层层递进,最终进入游戏场景!






写完了写完了终于写完了,虽然最后这篇写得最烂,只能靠注释,大家见谅。
最后附上我写了一堆注释的源码:http://yun.baidu.com/share/link?shareid=190898915&uk=1812511185*

顶起来,呵呵

你是工作人员吗,很好奇 :7:

又看了一遍,从一到五,楼主写的非常好,很有收获。谢谢。