pyqt5实现俄罗斯方块游戏

时间:2022-06-13 03:57:00

本章我们要制作一个俄罗斯方块游戏。

tetris

译注:称呼:方块是由四个小方格组成的

俄罗斯方块游戏是世界上最流行的游戏之一。是由一名叫alexey pajitnov的俄罗斯程序员在1985年制作的,从那时起,这个游戏就风靡了各个游戏平台。

俄罗斯方块归类为下落块迷宫游戏。游戏有7个基本形状:s、z、t、l、反向l、直线、方块,每个形状都由4个方块组成,方块最终都会落到屏幕底部。所以玩家通过控制形状的左右位置和旋转,让每个形状都以合适的位置落下,如果有一行全部被方块填充,这行就会消失,并且得分。游戏结束的条件是有形状接触到了屏幕顶部。

方块展示:

pyqt5实现俄罗斯方块游戏

pyqt5是专门为创建图形界面产生的,里面一些专门为制作游戏而开发的组件,所以pyqt5是能制作小游戏的。

制作电脑游戏也是提高自己编程能力的一种很好的方式。

开发

没有图片,所以就自己用绘画画出来几个图形。每个游戏里都有数学模型的,这个也是。

开工之前:

  • 用qtcore.qbasictimer()创建一个游戏循环
  • 模型是一直下落的
  • 模型的运动是以小块为基础单位的,不是按像素
  • 从数学意义上来说,模型就是就是一串数字而已

代码由四个类组成:tetris, board, tetrominoe和shape。tetris类创建游戏,board是游戏主要逻辑。tetrominoe包含了所有的砖块,shape是所有砖块的代码。

?
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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
#!/usr/bin/python3
# -*- coding: utf-8 -*-
 
"""
zetcode pyqt5 tutorial
this is a tetris game clone.
 
author: jan bodnar
website: zetcode.com
last edited: august 2017
"""
 
from pyqt5.qtwidgets import qmainwindow, qframe, qdesktopwidget, qapplication
from pyqt5.qtcore import qt, qbasictimer, pyqtsignal
from pyqt5.qtgui import qpainter, qcolor
import sys, random
 
class tetris(qmainwindow):
  
  def __init__(self):
    super().__init__()
    
    self.initui()
    
    
  def initui(self): 
    '''initiates application ui'''
 
    self.tboard = board(self)
    self.setcentralwidget(self.tboard)
 
    self.statusbar = self.statusbar()   
    self.tboard.msg2statusbar[str].connect(self.statusbar.showmessage)
    
    self.tboard.start()
    
    self.resize(180, 380)
    self.center()
    self.setwindowtitle('tetris')   
    self.show()
    
 
  def center(self):
    '''centers the window on the screen'''
    
    screen = qdesktopwidget().screengeometry()
    size = self.geometry()
    self.move((screen.width()-size.width())/2,
      (screen.height()-size.height())/2)
    
 
class board(qframe):
  
  msg2statusbar = pyqtsignal(str)
  
  boardwidth = 10
  boardheight = 22
  speed = 300
 
  def __init__(self, parent):
    super().__init__(parent)
    
    self.initboard()
    
    
  def initboard(self):  
    '''initiates board'''
 
    self.timer = qbasictimer()
    self.iswaitingafterline = false
    
    self.curx = 0
    self.cury = 0
    self.numlinesremoved = 0
    self.board = []
 
    self.setfocuspolicy(qt.strongfocus)
    self.isstarted = false
    self.ispaused = false
    self.clearboard()
    
    
  def shapeat(self, x, y):
    '''determines shape at the board position'''
    
    return self.board[(y * board.boardwidth) + x]
 
    
  def setshapeat(self, x, y, shape):
    '''sets a shape at the board'''
    
    self.board[(y * board.boardwidth) + x] = shape
    
 
  def squarewidth(self):
    '''returns the width of one square'''
    
    return self.contentsrect().width() // board.boardwidth
    
 
  def squareheight(self):
    '''returns the height of one square'''
    
    return self.contentsrect().height() // board.boardheight
    
 
  def start(self):
    '''starts game'''
    
    if self.ispaused:
      return
 
    self.isstarted = true
    self.iswaitingafterline = false
    self.numlinesremoved = 0
    self.clearboard()
 
    self.msg2statusbar.emit(str(self.numlinesremoved))
 
    self.newpiece()
    self.timer.start(board.speed, self)
 
    
  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()
 
    
  def paintevent(self, event):
    '''paints all shapes of the game'''
    
    painter = qpainter(self)
    rect = self.contentsrect()
 
    boardtop = rect.bottom() - board.boardheight * self.squareheight()
 
    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)
 
    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())
 
          
  def keypressevent(self, event):
    '''processes key press events'''
    
    if not self.isstarted or self.curpiece.shape() == tetrominoe.noshape:
      super(board, self).keypressevent(event)
      return
 
    key = event.key()
    
    if key == qt.key_p:
      self.pause()
      return
      
    if self.ispaused:
      return
        
    elif key == qt.key_left:
      self.trymove(self.curpiece, self.curx - 1, self.cury)
      
    elif key == qt.key_right:
      self.trymove(self.curpiece, self.curx + 1, self.cury)
      
    elif key == qt.key_down:
      self.trymove(self.curpiece.rotateright(), self.curx, self.cury)
      
    elif key == qt.key_up:
      self.trymove(self.curpiece.rotateleft(), self.curx, self.cury)
      
    elif key == qt.key_space:
      self.dropdown()
      
    elif key == qt.key_d:
      self.onelinedown()
      
    else:
      super(board, self).keypressevent(event)
        
 
  def timerevent(self, event):
    '''handles timer event'''
    
    if event.timerid() == self.timer.timerid():
      
      if self.iswaitingafterline:
        self.iswaitingafterline = false
        self.newpiece()
      else:
        self.onelinedown()
        
    else:
      super(board, self).timerevent(event)
 
      
  def clearboard(self):
    '''clears shapes from the board'''
    
    for i in range(board.boardheight * board.boardwidth):
      self.board.append(tetrominoe.noshape)
 
    
  def dropdown(self):
    '''drops down a shape'''
    
    newy = self.cury
    
    while newy > 0:
      
      if not self.trymove(self.curpiece, self.curx, newy - 1):
        break
        
      newy -= 1
 
    self.piecedropped()
    
 
  def onelinedown(self):
    '''goes one line down with a shape'''
    
    if not self.trymove(self.curpiece, self.curx, self.cury - 1):
      self.piecedropped()
      
 
  def piecedropped(self):
    '''after dropping shape, remove full lines and create new shape'''
    
    for i in range(4):
      
      x = self.curx + self.curpiece.x(i)
      y = self.cury - self.curpiece.y(i)
      self.setshapeat(x, y, self.curpiece.shape())
 
    self.removefulllines()
 
    if not self.iswaitingafterline:
      self.newpiece()
      
 
  def removefulllines(self):
    '''removes all full lines from the board'''
    
    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)
 
    if numfulllines > 0:
      
      self.numlinesremoved = self.numlinesremoved + numfulllines
      self.msg2statusbar.emit(str(self.numlinesremoved))
        
      self.iswaitingafterline = true
      self.curpiece.setshape(tetrominoe.noshape)
      self.update()
      
 
  def newpiece(self):
    '''creates a new shape'''
    
    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")
 
 
 
  def trymove(self, newpiece, newx, newy):
    '''tries to move a shape'''
    
    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
    
 
  def drawsquare(self, painter, x, y, shape):
    '''draws a square of a shape'''   
    
    colortable = [0x000000, 0xcc6666, 0x66cc66, 0x6666cc,
           0xcccc66, 0xcc66cc, 0x66cccc, 0xdaaa00]
 
    color = qcolor(colortable[shape])
    painter.fillrect(x + 1, y + 1, self.squarewidth() - 2,
      self.squareheight() - 2, color)
 
    painter.setpen(color.lighter())
    painter.drawline(x, y + self.squareheight() - 1, x, y)
    painter.drawline(x, y, x + self.squarewidth() - 1, y)
 
    painter.setpen(color.darker())
    painter.drawline(x + 1, y + self.squareheight() - 1,
      x + self.squarewidth() - 1, y + self.squareheight() - 1)
    painter.drawline(x + self.squarewidth() - 1,
      y + self.squareheight() - 1, x + self.squarewidth() - 1, y + 1)
 
 
class tetrominoe(object):
  
  noshape = 0
  zshape = 1
  sshape = 2
  lineshape = 3
  tshape = 4
  squareshape = 5
  lshape = 6
  mirroredlshape = 7
 
 
class shape(object):
  
  coordstable = (
    ((0, 0),   (0, 0),   (0, 0),   (0, 0)),
    ((0, -1),  (0, 0),   (-1, 0),  (-1, 1)),
    ((0, -1),  (0, 0),   (1, 0),   (1, 1)),
    ((0, -1),  (0, 0),   (0, 1),   (0, 2)),
    ((-1, 0),  (0, 0),   (1, 0),   (0, 1)),
    ((0, 0),   (1, 0),   (0, 1),   (1, 1)),
    ((-1, -1),  (0, -1),  (0, 0),   (0, 1)),
    ((1, -1),  (0, -1),  (0, 0),   (0, 1))
  )
 
  def __init__(self):
    
    self.coords = [[0,0] for i in range(4)]
    self.pieceshape = tetrominoe.noshape
 
    self.setshape(tetrominoe.noshape)
    
 
  def shape(self):
    '''returns shape'''
    
    return self.pieceshape
    
 
  def setshape(self, shape):
    '''sets a shape'''
    
    table = shape.coordstable[shape]
    
    for i in range(4):
      for j in range(2):
        self.coords[i][j] = table[i][j]
 
    self.pieceshape = shape
    
 
  def setrandomshape(self):
    '''chooses a random shape'''
    
    self.setshape(random.randint(1, 7))
 
    
  def x(self, index):
    '''returns x coordinate'''
    
    return self.coords[index][0]
 
    
  def y(self, index):
    '''returns y coordinate'''
    
    return self.coords[index][1]
 
    
  def setx(self, index, x):
    '''sets x coordinate'''
    
    self.coords[index][0] = x
 
    
  def sety(self, index, y):
    '''sets y coordinate'''
    
    self.coords[index][1] = y
 
    
  def minx(self):
    '''returns min x value'''
    
    m = self.coords[0][0]
    for i in range(4):
      m = min(m, self.coords[i][0])
 
    return m
 
    
  def maxx(self):
    '''returns max x value'''
    
    m = self.coords[0][0]
    for i in range(4):
      m = max(m, self.coords[i][0])
 
    return m
 
    
  def miny(self):
    '''returns min y value'''
    
    m = self.coords[0][1]
    for i in range(4):
      m = min(m, self.coords[i][1])
 
    return m
 
    
  def maxy(self):
    '''returns max y value'''
    
    m = self.coords[0][1]
    for i in range(4):
      m = max(m, self.coords[i][1])
 
    return m
 
    
  def rotateleft(self):
    '''rotates shape to the left'''
    
    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
 
    
  def rotateright(self):
    '''rotates shape to the right'''
    
    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
 
 
if __name__ == '__main__':
  
  app = qapplication([])
  tetris = tetris() 
  sys.exit(app.exec_())

游戏很简单,所以也就很好理解。程序加载之后游戏也就直接开始了,可以用p键暂停游戏,空格键让方块直接落到最下面。游戏的速度是固定的,并没有实现加速的功能。分数就是游戏中消除的行数。

?
1
2
self.tboard = board(self)
self.setcentralwidget(self.tboard)

创建了一个board类的实例,并设置为应用的中心组件。

?
1
2
self.statusbar = self.statusbar()   
self.tboard.msg2statusbar[str].connect(self.statusbar.showmessage)

创建一个statusbar来显示三种信息:消除的行数,游戏暂停状态或者游戏结束状态。msg2statusbar是一个自定义的信号,用在(和)board类(交互),showmessage()方法是一个内建的,用来在statusbar上显示信息的方法。

?
1
self.tboard.start()

初始化游戏:

?
1
2
3
4
class board(qframe):
  
  msg2statusbar = pyqtsignal(str)
...

创建了一个自定义信号msg2statusbar,当我们想往statusbar里显示信息的时候,发出这个信号就行了。

?
1
2
3
boardwidth = 10
boardheight = 22
speed = 300

这些是board类的变量。boardwidthboardheight分别是board的宽度和高度。speed是游戏的速度,每300ms出现一个新的方块。

?
1
2
3
4
5
6
...
self.curx = 0
self.cury = 0
self.numlinesremoved = 0
self.board = []
...

initboard()里初始化了一些重要的变量。self.board定义了方块的形状和位置,取值范围是0-7。

?
1
2
def shapeat(self, x, y):
  return self.board[(y * board.boardwidth) + x]

shapeat()决定了board里方块的的种类。

?
1
2
def squarewidth(self):
  return self.contentsrect().width() // board.boardwidth

board的大小可以动态的改变。所以方格的大小也应该随之变化。squarewidth()计算并返回每个块应该占用多少像素--也即board.boardwidth

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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上显示一条信息。

?
1
2
3
4
5
6
def paintevent(self, event):
  '''paints all shapes of the game'''
 
  painter = qpainter(self)
  rect = self.contentsrect()
...

渲染是在paintevent()方法里发生的qpainter负责pyqt5里所有低级绘画操作。

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

渲染游戏分为两步。第一步是先画出所有已经落在最下面的的图,这些保存在self.board里。可以使用shapeat()查看这个这个变量。

?
1
2
3
4
5
6
7
8
9
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())

第二步是画出更在下落的方块。

?
1
2
elif key == qt.key_right:
  self.trymove(self.curpiece, self.curx + 1, self.cury)

keypressevent()方法获得用户按下的按键。如果按下的是右方向键,就尝试把方块向右移动,说尝试是因为有可能到边界不能移动了。

?
1
2
elif key == qt.key_up:
  self.trymove(self.curpiece.rotateleft(), self.curx, self.cury)

上方向键是把方块向左旋转一下

?
1
2
elif key == qt.key_space:
  self.dropdown()

空格键会直接把方块放到底部

?
1
2
elif key == qt.key_d:
  self.onelinedown()

d键是加速一次下落速度。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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()是尝试移动方块的方法。如果方块已经到达board的边缘或者遇到了其他方块,就返回false。否则就把方块下落到想要

?
1
2
3
4
5
6
7
8
9
10
11
12
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)

在计时器事件里,要么是等一个方块下落完之后创建一个新的方块,要么是让一个方块直接落到底(move a falling piece one line down)。

?
1
2
3
4
def clearboard(self):
  
  for i in range(board.boardheight * board.boardwidth):
    self.board.append(tetrominoe.noshape)

clearboard()方法通过tetrominoe.noshape清空broad

?
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
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)
 ...

如果方块碰到了底部,就调用removefulllines()方法,找到所有能消除的行消除它们。消除的具体动作就是把符合条件的行消除掉之后,再把它上面的行下降一行。注意移除满行的动作是倒着来的,因为我们是按照重力来表现游戏的,如果不这样就有可能出现有些方块浮在空中的现象。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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()方法是用来创建形状随机的方块。如果随机的方块不能正确的出现在预设的位置,游戏结束。

?
1
2
3
4
5
6
7
8
9
10
class tetrominoe(object):
  
  noshape = 0
  zshape = 1
  sshape = 2
  lineshape = 3
  tshape = 4
  squareshape = 5
  lshape = 6
  mirroredlshape = 7

tetrominoe类保存了所有方块的形状。我们还定义了一个noshape的空形状。

shape类保存类方块内部的信息。

?
1
2
3
4
5
6
7
8
class shape(object):
  
  coordstable = (
    ((0, 0),   (0, 0),   (0, 0),   (0, 0)),
    ((0, -1),  (0, 0),   (-1, 0),  (-1, 1)),
    ...
  )
...

coordstable元组保存了所有的方块形状的组成。是一个构成方块的坐标模版。

?
1
self.coords = [[0,0] for i in range(4)]

上面创建了一个新的空坐标数组,这个数组将用来保存方块的坐标。

坐标系示意图:

pyqt5实现俄罗斯方块游戏

上面的图片可以帮助我们更好的理解坐标值的意义。比如元组(0, -1), (0, 0), (-1, 0), (-1, -1)代表了一个z形状的方块。这个图表就描绘了这个形状。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()方法向右旋转一个方块。正方形的方块就没必要旋转,就直接返回了。其他的是返回一个新的,能表示这个形状旋转了的坐标。

程序展示:

pyqt5实现俄罗斯方块游戏

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://segmentfault.com/a/1190000017845103