500 行代碼寫一個俄羅斯方塊游戲

導(dǎo)讀:本文我們要制作一個俄羅斯方塊游戲。

01 俄羅斯方塊 Tetris
俄羅斯方塊游戲是世界上最流行的游戲之一。是由一名叫Alexey Pajitnov的俄羅斯程序員在1985年制作的,從那時起,這個游戲就風(fēng)靡了各個游戲平臺。
俄羅斯方塊歸類為下落塊迷宮游戲。游戲有7個基本形狀:S、Z、T、L、反向L、直線、方塊,每個形狀都由4個方塊組成,方塊最終都會落到屏幕底部。所以玩家通過控制形狀的左右位置和旋轉(zhuǎn),讓每個形狀都以合適的位置落下,如果有一行全部被方塊填充,這行就會消失,并且得分。游戲結(jié)束的條件是有形狀接觸到了屏幕頂部。
方塊展示:

PyQt5是專門為創(chuàng)建圖形界面產(chǎn)生的,里面一些專門為制作游戲而開發(fā)的組件,所以PyQt5是能制作小游戲的。
制作電腦游戲也是提高自己編程能力的一種很好的方式。
02 開發(fā)
沒有圖片,所以就自己用繪畫畫出來幾個圖形。每個游戲里都有數(shù)學(xué)模型的,這個也是。
開工之前:
用
QtCore.QBasicTimer()QtCore.QBasicTimer()創(chuàng)建一個游戲循環(huán)模型是一直下落的
模型的運動是以小塊為基礎(chǔ)單位的,不是按像素
從數(shù)學(xué)意義上來說,模型就是就是一串數(shù)字而已
代碼由四個類組成:Tetris, Board, Tetrominoe和Shape。Tetris類創(chuàng)建游戲,Board是游戲主要邏輯。Tetrominoe包含了所有的磚塊,Shape是所有磚塊的代碼。
1#!/usr/bin/python3
2#?-*-?coding:?utf-8?-*-
3
4"""
5ZetCode?PyQt5?tutorial
6This?is?a?Tetris?game?clone.
7
8Author:?Jan?Bodnar
9Website:?zetcode.com
10Last?edited:?August?2017
11"""
12
13from?PyQt5.QtWidgets?import?QMainWindow,?QFrame,?QDesktopWidget,?QApplication
14from?PyQt5.QtCore?import?Qt,?QBasicTimer,?pyqtSignal
15from?PyQt5.QtGui?import?QPainter,?QColor
16import?sys,?random
17
18class?Tetris(QMainWindow):
19
20???def?__init__(self):
21???????super().__init__()
22
23???????self.initUI()
24
25
26???def?initUI(self):
27???????'''initiates?application?UI'''
28
29???????self.tboard?=?Board(self)
30???????self.setCentralWidget(self.tboard)
31
32???????self.statusbar?=?self.statusBar()
33???????self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)
34
35???????self.tboard.start()
36
37???????self.resize(180,?380)
38???????self.center()
39???????self.setWindowTitle('Tetris')
40???????self.show()
41
42
43???def?center(self):
44???????'''centers?the?window?on?the?screen'''
45
46???????screen?=?QDesktopWidget().screenGeometry()
47???????size?=?self.geometry()
48???????self.move((screen.width()-size.width())/2,
49???????????(screen.height()-size.height())/2)
50
51
52class?Board(QFrame):
53
54???msg2Statusbar?=?pyqtSignal(str)
55
56???BoardWidth?=?10
57???BoardHeight?=?22
58???Speed?=?300
59
60???def?__init__(self,?parent):
61???????super().__init__(parent)
62
63???????self.initBoard()
64
65
66???def?initBoard(self):
67???????'''initiates?board'''
68
69???????self.timer?=?QBasicTimer()
70???????self.isWaitingAfterLine?=?False
71
72???????self.curX?=?0
73???????self.curY?=?0
74???????self.numLinesRemoved?=?0
75???????self.board?=?[]
76
77???????self.setFocusPolicy(Qt.StrongFocus)
78???????self.isStarted?=?False
79???????self.isPaused?=?False
80???????self.clearBoard()
81
82
83???def?shapeAt(self,?x,?y):
84???????'''determines?shape?at?the?board?position'''
85
86???????return?self.board[(y?*?Board.BoardWidth)?+?x]
87
88
89???def?setShapeAt(self,?x,?y,?shape):
90???????'''sets?a?shape?at?the?board'''
91
92???????self.board[(y?*?Board.BoardWidth)?+?x]?=?shape
93
94
95???def?squareWidth(self):
96???????'''returns?the?width?of?one?square'''
97
98???????return?self.contentsRect().width()?//?Board.BoardWidth
99
100
101???def?squareHeight(self):
102???????'''returns?the?height?of?one?square'''
103
104???????return?self.contentsRect().height()?//?Board.BoardHeight
105
106
107???def?start(self):
108???????'''starts?game'''
109
110???????if?self.isPaused:
111???????????return
112
113???????self.isStarted?=?True
114???????self.isWaitingAfterLine?=?False
115???????self.numLinesRemoved?=?0
116???????self.clearBoard()
117
118???????self.msg2Statusbar.emit(str(self.numLinesRemoved))
119
120???????self.newPiece()
121???????self.timer.start(Board.Speed,?self)
122
123
124???def?pause(self):
125???????'''pauses?game'''
126
127???????if?not?self.isStarted:
128???????????return
129
130???????self.isPaused?=?not?self.isPaused
131
132???????if?self.isPaused:
133???????????self.timer.stop()
134???????????self.msg2Statusbar.emit("paused")
135
136???????else:
137???????????self.timer.start(Board.Speed,?self)
138???????????self.msg2Statusbar.emit(str(self.numLinesRemoved))
139
140???????self.update()
141
142
143???def?paintEvent(self,?event):
144???????'''paints?all?shapes?of?the?game'''
145
146???????painter?=?QPainter(self)
147???????rect?=?self.contentsRect()
148
149???????boardTop?=?rect.bottom()?-?Board.BoardHeight?*?self.squareHeight()
150
151???????for?i?in?range(Board.BoardHeight):
152???????????for?j?in?range(Board.BoardWidth):
153???????????????shape?=?self.shapeAt(j,?Board.BoardHeight?-?i?-?1)
154
155???????????????if?shape?!=?Tetrominoe.NoShape:
156???????????????????self.drawSquare(painter,
157???????????????????????rect.left()?+?j?*?self.squareWidth(),
158???????????????????????boardTop?+?i?*?self.squareHeight(),?shape)
159
160???????if?self.curPiece.shape()?!=?Tetrominoe.NoShape:
161
162???????????for?i?in?range(4):
163
164???????????????x?=?self.curX?+?self.curPiece.x(i)
165???????????????y?=?self.curY?-?self.curPiece.y(i)
166???????????????self.drawSquare(painter,?rect.left()?+?x?*?self.squareWidth(),
167???????????????????boardTop?+?(Board.BoardHeight?-?y?-?1)?*?self.squareHeight(),
168???????????????????self.curPiece.shape())
169
170
171???def?keyPressEvent(self,?event):
172???????'''processes?key?press?events'''
173
174???????if?not?self.isStarted?or?self.curPiece.shape()?==?Tetrominoe.NoShape:
175???????????super(Board,?self).keyPressEvent(event)
176???????????return
177
178???????key?=?event.key()
179
180???????if?key?==?Qt.Key_P:
181???????????self.pause()
182???????????return
183
184???????if?self.isPaused:
185???????????return
186
187???????elif?key?==?Qt.Key_Left:
188???????????self.tryMove(self.curPiece,?self.curX?-?1,?self.curY)
189
190???????elif?key?==?Qt.Key_Right:
191???????????self.tryMove(self.curPiece,?self.curX?+?1,?self.curY)
192
193???????elif?key?==?Qt.Key_Down:
194???????????self.tryMove(self.curPiece.rotateRight(),?self.curX,?self.curY)
195
196???????elif?key?==?Qt.Key_Up:
197???????????self.tryMove(self.curPiece.rotateLeft(),?self.curX,?self.curY)
198
199???????elif?key?==?Qt.Key_Space:
200???????????self.dropDown()
201
202???????elif?key?==?Qt.Key_D:
203???????????self.oneLineDown()
204
205???????else:
206???????????super(Board,?self).keyPressEvent(event)
207
208
209???def?timerEvent(self,?event):
210???????'''handles?timer?event'''
211
212???????if?event.timerId()?==?self.timer.timerId():
213
214???????????if?self.isWaitingAfterLine:
215???????????????self.isWaitingAfterLine?=?False
216???????????????self.newPiece()
217???????????else:
218???????????????self.oneLineDown()
219
220???????else:
221???????????super(Board,?self).timerEvent(event)
222
223
224???def?clearBoard(self):
225???????'''clears?shapes?from?the?board'''
226
227???????for?i?in?range(Board.BoardHeight?*?Board.BoardWidth):
228???????????self.board.append(Tetrominoe.NoShape)
229
230
231???def?dropDown(self):
232???????'''drops?down?a?shape'''
233
234???????newY?=?self.curY
235
236???????while?newY?>?0:
237
238???????????if?not?self.tryMove(self.curPiece,?self.curX,?newY?-?1):
239???????????????break
240
241???????????newY?-=?1
242
243???????self.pieceDropped()
244
245
246???def?oneLineDown(self):
247???????'''goes?one?line?down?with?a?shape'''
248
249???????if?not?self.tryMove(self.curPiece,?self.curX,?self.curY?-?1):
250???????????self.pieceDropped()
251
252
253???def?pieceDropped(self):
254???????'''after?dropping?shape,?remove?full?lines?and?create?new?shape'''
255
256???????for?i?in?range(4):
257
258???????????x?=?self.curX?+?self.curPiece.x(i)
259???????????y?=?self.curY?-?self.curPiece.y(i)
260???????????self.setShapeAt(x,?y,?self.curPiece.shape())
261
262???????self.removeFullLines()
263
264???????if?not?self.isWaitingAfterLine:
265???????????self.newPiece()
266
267
268???def?removeFullLines(self):
269???????'''removes?all?full?lines?from?the?board'''
270
271???????numFullLines?=?0
272???????rowsToRemove?=?[]
273
274???????for?i?in?range(Board.BoardHeight):
275
276???????????n?=?0
277???????????for?j?in?range(Board.BoardWidth):
278???????????????if?not?self.shapeAt(j,?i)?==?Tetrominoe.NoShape:
279???????????????????n?=?n?+?1
280
281???????????if?n?==?10:
282???????????????rowsToRemove.append(i)
283
284???????rowsToRemove.reverse()
285
286
287???????for?m?in?rowsToRemove:
288
289???????????for?k?in?range(m,?Board.BoardHeight):
290???????????????for?l?in?range(Board.BoardWidth):
291???????????????????????self.setShapeAt(l,?k,?self.shapeAt(l,?k?+?1))
292
293???????numFullLines?=?numFullLines?+?len(rowsToRemove)
294
295???????if?numFullLines?>?0:
296
297???????????self.numLinesRemoved?=?self.numLinesRemoved?+?numFullLines
298???????????self.msg2Statusbar.emit(str(self.numLinesRemoved))
299
300???????????self.isWaitingAfterLine?=?True
301???????????self.curPiece.setShape(Tetrominoe.NoShape)
302???????????self.update()
303
304
305???def?newPiece(self):
306???????'''creates?a?new?shape'''
307
308???????self.curPiece?=?Shape()
309???????self.curPiece.setRandomShape()
310???????self.curX?=?Board.BoardWidth?//?2?+?1
311???????self.curY?=?Board.BoardHeight?-?1?+?self.curPiece.minY()
312
313???????if?not?self.tryMove(self.curPiece,?self.curX,?self.curY):
314
315???????????self.curPiece.setShape(Tetrominoe.NoShape)
316???????????self.timer.stop()
317???????????self.isStarted?=?False
318???????????self.msg2Statusbar.emit("Game?over")
319
320
321
322???def?tryMove(self,?newPiece,?newX,?newY):
323???????'''tries?to?move?a?shape'''
324
325???????for?i?in?range(4):
326
327???????????x?=?newX?+?newPiece.x(i)
328???????????y?=?newY?-?newPiece.y(i)
329
330???????????if?x?0?or?x?>=?Board.BoardWidth?or?y?0?or?y?>=?Board.BoardHeight:
331???????????????return?False
332
333???????????if?self.shapeAt(x,?y)?!=?Tetrominoe.NoShape:
334???????????????return?False
335
336???????self.curPiece?=?newPiece
337???????self.curX?=?newX
338???????self.curY?=?newY
339???????self.update()
340
341???????return?True
342
343
344???def?drawSquare(self,?painter,?x,?y,?shape):
345???????'''draws?a?square?of?a?shape'''
346
347???????colorTable?=?[0x000000,?0xCC6666,?0x66CC66,?0x6666CC,
348?????????????????????0xCCCC66,?0xCC66CC,?0x66CCCC,?0xDAAA00]
349
350???????color?=?QColor(colorTable[shape])
351???????painter.fillRect(x?+?1,?y?+?1,?self.squareWidth()?-?2,
352???????????self.squareHeight()?-?2,?color)
353
354???????painter.setPen(color.lighter())
355???????painter.drawLine(x,?y?+?self.squareHeight()?-?1,?x,?y)
356???????painter.drawLine(x,?y,?x?+?self.squareWidth()?-?1,?y)
357
358???????painter.setPen(color.darker())
359???????painter.drawLine(x?+?1,?y?+?self.squareHeight()?-?1,
360???????????x?+?self.squareWidth()?-?1,?y?+?self.squareHeight()?-?1)
361???????painter.drawLine(x?+?self.squareWidth()?-?1,
362???????????y?+?self.squareHeight()?-?1,?x?+?self.squareWidth()?-?1,?y?+?1)
363
364
365class?Tetrominoe(object):
366
367???NoShape?=?0
368???ZShape?=?1
369???SShape?=?2
370???LineShape?=?3
371???TShape?=?4
372???SquareShape?=?5
373???LShape?=?6
374???MirroredLShape?=?7
375
376
377class?Shape(object):
378
379???coordsTable?=?(
380???????((0,?0),?????(0,?0),?????(0,?0),?????(0,?0)),
381???????((0,?-1),????(0,?0),?????(-1,?0),????(-1,?1)),
382???????((0,?-1),????(0,?0),?????(1,?0),?????(1,?1)),
383???????((0,?-1),????(0,?0),?????(0,?1),?????(0,?2)),
384???????((-1,?0),????(0,?0),?????(1,?0),?????(0,?1)),
385???????((0,?0),?????(1,?0),?????(0,?1),?????(1,?1)),
386???????((-1,?-1),???(0,?-1),????(0,?0),?????(0,?1)),
387???????((1,?-1),????(0,?-1),????(0,?0),?????(0,?1))
388???)
389
390???def?__init__(self):
391
392???????self.coords?=?[[0,0]?for?i?in?range(4)]
393???????self.pieceShape?=?Tetrominoe.NoShape
394
395???????self.setShape(Tetrominoe.NoShape)
396
397
398???def?shape(self):
399???????'''returns?shape'''
400
401???????return?self.pieceShape
402
403
404???def?setShape(self,?shape):
405???????'''sets?a?shape'''
406
407???????table?=?Shape.coordsTable[shape]
408
409???????for?i?in?range(4):
410???????????for?j?in?range(2):
411???????????????self.coords[i][j]?=?table[i][j]
412
413???????self.pieceShape?=?shape
414
415
416???def?setRandomShape(self):
417???????'''chooses?a?random?shape'''
418
419???????self.setShape(random.randint(1,?7))
420
421
422???def?x(self,?index):
423???????'''returns?x?coordinate'''
424
425???????return?self.coords[index][0]
426
427
428???def?y(self,?index):
429???????'''returns?y?coordinate'''
430
431???????return?self.coords[index][1]
432
433
434???def?setX(self,?index,?x):
435???????'''sets?x?coordinate'''
436
437???????self.coords[index][0]?=?x
438
439
440???def?setY(self,?index,?y):
441???????'''sets?y?coordinate'''
442
443???????self.coords[index][1]?=?y
444
445
446???def?minX(self):
447???????'''returns?min?x?value'''
448
449???????m?=?self.coords[0][0]
450???????for?i?in?range(4):
451???????????m?=?min(m,?self.coords[i][0])
452
453???????return?m
454
455
456???def?maxX(self):
457???????'''returns?max?x?value'''
458
459???????m?=?self.coords[0][0]
460???????for?i?in?range(4):
461???????????m?=?max(m,?self.coords[i][0])
462
463???????return?m
464
465
466???def?minY(self):
467???????'''returns?min?y?value'''
468
469???????m?=?self.coords[0][1]
470???????for?i?in?range(4):
471???????????m?=?min(m,?self.coords[i][1])
472
473???????return?m
474
475
476???def?maxY(self):
477???????'''returns?max?y?value'''
478
479???????m?=?self.coords[0][1]
480???????for?i?in?range(4):
481???????????m?=?max(m,?self.coords[i][1])
482
483???????return?m
484
485
486???def?rotateLeft(self):
487???????'''rotates?shape?to?the?left'''
488
489???????if?self.pieceShape?==?Tetrominoe.SquareShape:
490???????????return?self
491
492???????result?=?Shape()
493???????result.pieceShape?=?self.pieceShape
494
495???????for?i?in?range(4):
496
497???????????result.setX(i,?self.y(i))
498???????????result.setY(i,?-self.x(i))
499
500???????return?result
501
502
503???def?rotateRight(self):
504???????'''rotates?shape?to?the?right'''
505
506???????if?self.pieceShape?==?Tetrominoe.SquareShape:
507???????????return?self
508
509???????result?=?Shape()
510???????result.pieceShape?=?self.pieceShape
511
512???????for?i?in?range(4):
513
514???????????result.setX(i,?-self.y(i))
515???????????result.setY(i,?self.x(i))
516
517???????return?result
518
519
520if?__name__?==?'__main__':
521
522???app?=?QApplication([])
523???tetris?=?Tetris()
524???sys.exit(app.exec_())
(代碼可以左右滑動)
游戲很簡單,所以也就很好理解。程序加載之后游戲也就直接開始了,可以用P鍵暫停游戲,空格鍵讓方塊直接落到最下面。游戲的速度是固定的,并沒有實現(xiàn)加速的功能。分數(shù)就是游戲中消除的行數(shù)。
self.tboard?=?Board(self)
self.setCentralWidget(self.tboard)
創(chuàng)建了一個Board類的實例,并設(shè)置為應(yīng)用的中心組件。
self.statusbar?=?self.statusBar()
self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)
創(chuàng)建一個statusbar來顯示三種信息:消除的行數(shù),游戲暫停狀態(tài)或者游戲結(jié)束狀態(tài)。msg2Statusbar是一個自定義的信號,用在(和)Board類(交互),showMessage()方法是一個內(nèi)建的,用來在statusbar上顯示信息的方法。
self.tboard.start()
初始化游戲:
class?Board(QFrame):
???msg2Statusbar?=?pyqtSignal(str)
...???
創(chuàng)建了一個自定義信號msg2Statusbar,當我們想往statusbar里顯示信息的時候,發(fā)出這個信號就行了。
BoardWidth?=?10
BoardHeight?=?22
Speed?=?300
這些是Board類的變量。BoardWidth和BoardHeight分別是board的寬度和高度。Speed是游戲的速度,每300ms出現(xiàn)一個新的方塊。
...
self.curX?=?0
self.curY?=?0
self.numLinesRemoved?=?0
self.board?=?[]
...
在initBoard()里初始化了一些重要的變量。self.board定義了方塊的形狀和位置,取值范圍是0-7。
def?shapeAt(self,?x,?y):
???return?self.board[(y?*?Board.BoardWidth)?+?x]
shapeAt()決定了board里方塊的的種類。
def?squareWidth(self):
???return?self.contentsRect().width()?//?Board.BoardWidth
board的大小可以動態(tài)的改變。所以方格的大小也應(yīng)該隨之變化。squareWidth()計算并返回每個塊應(yīng)該占用多少像素--也即Board.BoardWidth。
def?pause(self):
???'''pauses?game'''
???if?not?self.isStarted:
???????return
???self.isPaused?=?not?self.isPaused
???if?self.isPaused:
???????self.timer.stop()
???????self.msg2Statusbar.emit("paused")
???else:
???????self.timer.start(Board.Speed,?self)
???????self.msg2Statusbar.emit(str(self.numLinesRemoved))
???self.update()
pause()方法用來暫停游戲,停止計時并在statusbar上顯示一條信息。
def?paintEvent(self,?event):
???'''paints?all?shapes?of?the?game'''
???painter?=?QPainter(self)
???rect?=?self.contentsRect()
...
渲染是在paintEvent()方法里發(fā)生的QPainter負責(zé)PyQt5里所有低級繪畫操作。
for?i?in?range(Board.BoardHeight):
???for?j?in?range(Board.BoardWidth):
???????shape?=?self.shapeAt(j,?Board.BoardHeight?-?i?-?1)
???????if?shape?!=?Tetrominoe.NoShape:
???????????self.drawSquare(painter,
???????????????rect.left()?+?j?*?self.squareWidth(),
???????????????boardTop?+?i?*?self.squareHeight(),?shape)
渲染游戲分為兩步。第一步是先畫出所有已經(jīng)落在最下面的的圖,這些保存在self.board里??梢允褂?/span>shapeAt()查看這個這個變量。
if?self.curPiece.shape()?!=?Tetrominoe.NoShape:
???for?i?in?range(4):
???????x?=?self.curX?+?self.curPiece.x(i)
???????y?=?self.curY?-?self.curPiece.y(i)
???????self.drawSquare(painter,?rect.left()?+?x?*?self.squareWidth(),
???????????boardTop?+?(Board.BoardHeight?-?y?-?1)?*?self.squareHeight(),
???????????self.curPiece.shape())
第二步是畫出更在下落的方塊。
elif?key?==?Qt.Key_Right:
???self.tryMove(self.curPiece,?self.curX?+?1,?self.curY)
在keyPressEvent()方法獲得用戶按下的按鍵。如果按下的是右方向鍵,就嘗試把方塊向右移動,說嘗試是因為有可能到邊界不能移動了。
elif?key?==?Qt.Key_Up:
???self.tryMove(self.curPiece.rotateLeft(),?self.curX,?self.curY)
上方向鍵是把方塊向左旋轉(zhuǎn)一下
elif?key?==?Qt.Key_Space:
???self.dropDown()
空格鍵會直接把方塊放到底部
elif?key?==?Qt.Key_D:
???self.oneLineDown()
D鍵是加速一次下落速度。
def?tryMove(self,?newPiece,?newX,?newY):
???for?i?in?range(4):
???????x?=?newX?+?newPiece.x(i)
???????y?=?newY?-?newPiece.y(i)
???????if?x?0?or?x?>=?Board.BoardWidth?or?y?0?or?y?>=?Board.BoardHeight:
???????????return?False
???????if?self.shapeAt(x,?y)?!=?Tetrominoe.NoShape:
???????????return?False
???self.curPiece?=?newPiece
???self.curX?=?newX
???self.curY?=?newY
???self.update()
???return?True
tryMove()是嘗試移動方塊的方法。如果方塊已經(jīng)到達board的邊緣或者遇到了其他方塊,就返回False。否則就把方塊下落到想要
def?timerEvent(self,?event):
???if?event.timerId()?==?self.timer.timerId():
???????if?self.isWaitingAfterLine:
???????????self.isWaitingAfterLine?=?False
???????????self.newPiece()
???????else:
???????????self.oneLineDown()
???else:
???????super(Board,?self).timerEvent(event)
在計時器事件里,要么是等一個方塊下落完之后創(chuàng)建一個新的方塊,要么是讓一個方塊直接落到底(move a falling piece one line down)。
def?clearBoard(self):
???for?i?in?range(Board.BoardHeight?*?Board.BoardWidth):
???????self.board.append(Tetrominoe.NoShape)
clearBoard()方法通過Tetrominoe.NoShape清空broad。
def?removeFullLines(self):
???numFullLines?=?0
???rowsToRemove?=?[]
???for?i?in?range(Board.BoardHeight):
???????n?=?0
???????for?j?in?range(Board.BoardWidth):
???????????if?not?self.shapeAt(j,?i)?==?Tetrominoe.NoShape:
???????????????n?=?n?+?1
???????if?n?==?10:
???????????rowsToRemove.append(i)
???rowsToRemove.reverse()
???for?m?in?rowsToRemove:
???????for?k?in?range(m,?Board.BoardHeight):
???????????for?l?in?range(Board.BoardWidth):
???????????????????self.setShapeAt(l,?k,?self.shapeAt(l,?k?+?1))
???numFullLines?=?numFullLines?+?len(rowsToRemove)
...
如果方塊碰到了底部,就調(diào)用removeFullLines()方法,找到所有能消除的行消除它們。消除的具體動作就是把符合條件的行消除掉之后,再把它上面的行下降一行。注意移除滿行的動作是倒著來的,因為我們是按照重力來表現(xiàn)游戲的,如果不這樣就有可能出現(xiàn)有些方塊浮在空中的現(xiàn)象。
def?newPiece(self):
???self.curPiece?=?Shape()
???self.curPiece.setRandomShape()
???self.curX?=?Board.BoardWidth?//?2?+?1
???self.curY?=?Board.BoardHeight?-?1?+?self.curPiece.minY()
???if?not?self.tryMove(self.curPiece,?self.curX,?self.curY):
???????self.curPiece.setShape(Tetrominoe.NoShape)
???????self.timer.stop()
???????self.isStarted?=?False
???????self.msg2Statusbar.emit("Game?over")
newPiece()方法是用來創(chuàng)建形狀隨機的方塊。如果隨機的方塊不能正確的出現(xiàn)在預(yù)設(shè)的位置,游戲結(jié)束。
class?Tetrominoe(object):
???NoShape?=?0
???ZShape?=?1
???SShape?=?2
???LineShape?=?3
???TShape?=?4
???SquareShape?=?5
???LShape?=?6
???MirroredLShape?=?7
Tetrominoe類保存了所有方塊的形狀。我們還定義了一個NoShape的空形狀。
Shape類保存類方塊內(nèi)部的信息。
class?Shape(object):
???coordsTable?=?(
???????((0,?0),?????(0,?0),?????(0,?0),?????(0,?0)),
???????((0,?-1),????(0,?0),?????(-1,?0),????(-1,?1)),
???????...
???)
...????
coordsTable元組保存了所有的方塊形狀的組成。是一個構(gòu)成方塊的坐標模版。
self.coords?=?[[0,0]?for?i?in?range(4)]??
上面創(chuàng)建了一個新的空坐標數(shù)組,這個數(shù)組將用來保存方塊的坐標。
坐標系示意圖:

上面的圖片可以幫助我們更好的理解坐標值的意義。比如元組(0, -1), (0, 0), (-1, 0), (-1, -1)代表了一個Z形狀的方塊。這個圖表就描繪了這個形狀。
def?rotateLeft(self):
???if?self.pieceShape?==?Tetrominoe.SquareShape:
???????return?self
???result?=?Shape()
???result.pieceShape?=?self.pieceShape
???for?i?in?range(4):
???????result.setX(i,?self.y(i))
???????result.setY(i,?-self.x(i))
???return?result
rotateLeft()方法向右旋轉(zhuǎn)一個方塊。正方形的方塊就沒必要旋轉(zhuǎn),就直接返回了。其他的是返回一個新的,能表示這個形狀旋轉(zhuǎn)了的坐標。
程序展示:

---
推薦閱讀:
完全整理 | 365篇高質(zhì)技術(shù)文章目錄整理
專注服務(wù)器后臺技術(shù)棧知識總結(jié)分享
歡迎關(guān)注交流共同進步
碼農(nóng)有道?coding
碼農(nóng)有道,為您提供通俗易懂的技術(shù)文章,讓技術(shù)變的更簡單
嘿,你在看嗎
