一、最后的前言
依旧使用久方法:顺藤摸瓜,一点一点发现,打开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(单元),如下图:
包含了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中增加了指示灯的动作效果。
这就是指示灯:
同时,扩增了一个方法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 |
总结起来就当触摸结束时,结束点敲中按钮,将会触发事件,层层递进,最终进入游戏场景!
最后附上带注释的源码:mycoinflip.zip