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

来源:互联网 发布:免费财务分析软件 编辑:程序博客网 时间:2024/05/29 02:29

一、最后的前言

依旧使用久方法:顺藤摸瓜,一点一点发现,打开ChooseLevelScene.lua吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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

不难,唯一需要继续深入的就是这段代码:

1
2
3
4
5
-- 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)


其实,想要顺着代码往看下,我们将会把要这个游戏剩下的五个脚本关联起来:

1
2
3
4
5
LevelsList.lua
LevelsListCell.lua
PageControl.lua
ScrollView.lua
ScrollViewCell.lua

只要明白了这五个脚本,就懂得了该游戏的关卡选择场景是如何建立的,进而整个游戏也就完整了解了一遍。先让大家了解下我对这最后的五个脚本理解的关系:

  • LevelsList继承自→PageControl继承自→ScrollView

  • LevelsListCell继承自→ScrollViewCell

  • LevelsList在内部使用了LevelsListCell

让我们先从简单的讲起,就是cell(单元),先明白,该游戏运用时,一个页面就是一个cell(单元),如下图:

56_371891_6b9ab3597cef249.png

包含了1到16有一个看不见的矩形,它就一个cell(单元),你向左滑动一下就会进入下一个cell(单元):17到32


二、cell(单元) 

从ScrollViewCell.lua开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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<i>
        -- 如果传入的点在该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}) 意思就委派一个Event,其中name属性是必须,作为事件的名字,但要触发该事件时就需要用到,而其他属性可以任意。这句代码结束后就相当LevelsListCell 这个类有了一个Event,可以看做一个表event={{name = "onTapLevelIcon", levelIndex = button.levelIndex}}


三、滚动控件ScrollView.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
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[self.currentIndex]
    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[index]
    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[i - 1]
        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<i>
        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),原本该方法只在未点中时输出信息,但重写后会根据触摸偏移量进行翻页动作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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<i>
            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<i>
            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中增加了指示灯的动作效果。

这就是指示灯:

56_371891_d9ebb258befb5e3.png

同时,扩增了一个方法onTapLevelIcon,作用是委派事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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


六、使用

回到最初的代码:

1
2
3
4
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:

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

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


七、再讲讲dispatchEvent

先说清楚,这个地方完全了看着这个游戏代码自我理解,不太能讲得明白,大家可以参考这篇文章:《解析Quick-Cocos2d-x中自定义事件》


还是继续看这句代码:

1
self.levelsList:addEventListener("onTapLevelIcon", handler(self, self.onTapLevelIcon))

问题来了,事件"onTapLevelIcon"哪里来的呢?回到levelsList.lua发现:

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

并且在ctor中发现了使用它的代码:

1
cell:addEventListener("onTapLevelIcon", function(event) return self:onTapLevelIcon(event) end)

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


但在这里,我们又发现了Cell里也有一个"onTapLevelIcon"事件,好吧,乖乖回去LevelsListCell

1
2
3
4
5
6
7
-- 触摸敲中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:

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

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

0 0
原创粉丝点击