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

一、最后的前言

依旧使用久方法:顺藤摸瓜,一点一点发现,打开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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值