国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - Python使用pygame模塊編寫俄羅斯方塊游戲的代碼實例

Python使用pygame模塊編寫俄羅斯方塊游戲的代碼實例

2020-08-04 11:00腳本之家 Python

這篇文章主要介紹了Python使用pygame模塊編寫俄羅斯方塊游戲的代碼實例,最基本的方塊變換和行消除等功能都在代碼中一一體現,需要的朋友可以參考下

文章先介紹了關于俄羅斯方塊游戲的幾個術語。

  • 邊框——由10*20個空格組成,方塊就落在這里面。
  • 盒子——組成方塊的其中小方塊,是組成方塊的基本單元。
  • 方塊——從邊框頂掉下的東西,游戲者可以翻轉和改變位置。每個方塊由4個盒子組成。
  • 形狀——不同類型的方塊。這里形狀的名字被叫做T, S, Z ,J, L, I , O。如下圖所示:

Python使用pygame模塊編寫俄羅斯方塊游戲的代碼實例

模版——用一個列表存放形狀被翻轉后的所有可能樣式。全部存放在變量里,變量名字如S_SHAPE_TEMPLATE or J_SHAPE_TEMPLATE
著陸——當一個方塊到達邊框的底部或接觸到在其他的盒子話,我們就說這個方塊著陸了。那樣的話,另一個方塊就會開始下落。
下面先把代碼敲一遍,試著了解作者意圖,體會俄羅斯方塊游戲的制作過程。

?
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
import random, time, pygame, sys
from pygame.locals import *
 
FPS = 25
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
BOXSIZE = 20
BOARDWIDTH = 10
BOARDHEIGHT = 20
BLANK = '.'
 
MOVESIDEWAYSFREQ = 0.15
MOVEDOWNFREQ = 0.1
 
XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2)
TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5
#        R  G  B
WHITE    = (255, 255, 255)
GRAY    = (185, 185, 185)
BLACK    = ( 000)
RED     = (15500)
LIGHTRED  = (175, 20, 20)
GREEN    = ( 0, 1550)
LIGHTGREEN = ( 20, 175, 20)
BLUE    = ( 00, 155)
LIGHTBLUE  = ( 20, 20, 175)
YELLOW   = (155, 1550)
LIGHTYELLOW = (175, 175, 20)
 
BORDERCOLOR = BLUE
BGCOLOR = BLACK
TEXTCOLOR = WHITE
TEXTSHADOWCOLOR = GRAY
COLORS   = (   BLUE,   GREEN,   RED,   YELLOW)
LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW)
assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color
 
TEMPLATEWIDTH = 5
TEMPLATEHEIGHT = 5
 
S_SHAPE_TEMPLATE = [['.....',
           '.....',
           '..OO.',
           '.OO..',
           '.....'],
          ['.....',
           '..O..',
           '..OO.',
           '...O.',
           '.....']]
 
Z_SHAPE_TEMPLATE = [['.....',
           '.....',
           '.OO..',
           '..OO.',
           '.....'],
          ['.....',
           '..O..',
           '.OO..',
           '.O...',
           '.....']]
 
I_SHAPE_TEMPLATE = [['..O..',
           '..O..',
           '..O..',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           'OOOO.',
           '.....',
           '.....']]
 
O_SHAPE_TEMPLATE = [['.....',
           '.....',
           '.OO..',
           '.OO..',
           '.....']]
 
J_SHAPE_TEMPLATE = [['.....',
           '.O...',
           '.OOO.',
           '.....',
           '.....'],
          ['.....',
           '..OO.',
           '..O..',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           '.OOO.',
           '...O.',
           '.....'],
          ['.....',
           '..O..',
           '..O..',
           '.OO..',
           '.....']]
 
L_SHAPE_TEMPLATE = [['.....',
           '...O.',
           '.OOO.',
           '.....',
           '.....'],
          ['.....',
           '..O..',
           '..O..',
           '..OO.',
           '.....'],
          ['.....',
           '.....',
           '.OOO.',
           '.O...',
           '.....'],
          ['.....',
           '.OO..',
           '..O..',
           '..O..',
           '.....']]
 
T_SHAPE_TEMPLATE = [['.....',
           '..O..',
           '.OOO.',
           '.....',
           '.....'],
          ['.....',
           '..O..',
           '..OO.',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           '.OOO.',
           '..O..',
           '.....'],
          ['.....',
           '..O..',
           '.OO..',
           '..O..',
           '.....']]
 
PIECES = {'S': S_SHAPE_TEMPLATE,
     'Z': Z_SHAPE_TEMPLATE,
     'J': J_SHAPE_TEMPLATE,
     'L': L_SHAPE_TEMPLATE,
     'I': I_SHAPE_TEMPLATE,
     'O': O_SHAPE_TEMPLATE,
     'T': T_SHAPE_TEMPLATE}
 
 
def main():
  global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT
  pygame.init()
  FPSCLOCK = pygame.time.Clock()
  DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
  BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
  BIGFONT = pygame.font.Font('freesansbold.ttf', 100)
  pygame.display.set_caption('Tetromino')
 
  showTextScreen('Tetromino')
  while True: # game loop
    if random.randint(0, 1) == 0:
      pygame.mixer.music.load('tetrisb.mid')
    else:
      pygame.mixer.music.load('tetrisc.mid')
    pygame.mixer.music.play(-1, 0.0)
    runGame()
    pygame.mixer.music.stop()
    showTextScreen('Game Over')
 
 
def runGame():
  # setup variables for the start of the game
  board = getBlankBoard()
  lastMoveDownTime = time.time()
  lastMoveSidewaysTime = time.time()
  lastFallTime = time.time()
  movingDown = False # note: there is no movingUp variable
  movingLeft = False
  movingRight = False
  score = 0
  level, fallFreq = calculateLevelAndFallFreq(score)
 
  fallingPiece = getNewPiece()
  nextPiece = getNewPiece()
 
  while True: # game loop
    if fallingPiece == None:
      # No falling piece in play, so start a new piece at the top
      fallingPiece = nextPiece
      nextPiece = getNewPiece()
      lastFallTime = time.time() # reset lastFallTime
 
      if not isValidPosition(board, fallingPiece):
        return # can't fit a new piece on the board, so game over
 
    checkForQuit()
    for event in pygame.event.get(): # event handling loop
      if event.type == KEYUP:
        if (event.key == K_p):
          # Pausing the game
          DISPLAYSURF.fill(BGCOLOR)
          pygame.mixer.music.stop()
          showTextScreen('Paused') # pause until a key press
          pygame.mixer.music.play(-1, 0.0)
          lastFallTime = time.time()
          lastMoveDownTime = time.time()
          lastMoveSidewaysTime = time.time()
        elif (event.key == K_LEFT or event.key == K_a):
          movingLeft = False
        elif (event.key == K_RIGHT or event.key == K_d):
          movingRight = False
        elif (event.key == K_DOWN or event.key == K_s):
          movingDown = False
 
      elif event.type == KEYDOWN:
        # moving the piece sideways
        if (event.key == K_LEFT or event.key == K_a) and isValidPosition(board, fallingPiece, adjX=-1):
          fallingPiece['x'] -= 1
          movingLeft = True
          movingRight = False
          lastMoveSidewaysTime = time.time()
 
        elif (event.key == K_RIGHT or event.key == K_d) and isValidPosition(board, fallingPiece, adjX=1):
          fallingPiece['x'] += 1
          movingRight = True
          movingLeft = False
          lastMoveSidewaysTime = time.time()
 
        # rotating the piece (if there is room to rotate)
        elif (event.key == K_UP or event.key == K_w):
          fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
          if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
        elif (event.key == K_q): # rotate the other direction
          fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
          if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
 
        # making the piece fall faster with the down key
        elif (event.key == K_DOWN or event.key == K_s):
          movingDown = True
          if isValidPosition(board, fallingPiece, adjY=1):
            fallingPiece['y'] += 1
          lastMoveDownTime = time.time()
 
        # move the current piece all the way down
        elif event.key == K_SPACE:
          movingDown = False
          movingLeft = False
          movingRight = False
          for i in range(1, BOARDHEIGHT):
            if not isValidPosition(board, fallingPiece, adjY=i):
              break
          fallingPiece['y'] += i - 1
 
    # handle moving the piece because of user input
    if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ:
      if movingLeft and isValidPosition(board, fallingPiece, adjX=-1):
        fallingPiece['x'] -= 1
      elif movingRight and isValidPosition(board, fallingPiece, adjX=1):
        fallingPiece['x'] += 1
      lastMoveSidewaysTime = time.time()
 
    if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1):
      fallingPiece['y'] += 1
      lastMoveDownTime = time.time()
 
    # let the piece fall if it is time to fall
    if time.time() - lastFallTime > fallFreq:
      # see if the piece has landed
      if not isValidPosition(board, fallingPiece, adjY=1):
        # falling piece has landed, set it on the board
        addToBoard(board, fallingPiece)
        score += removeCompleteLines(board)
        level, fallFreq = calculateLevelAndFallFreq(score)
        fallingPiece = None
      else:
        # piece did not land, just move the piece down
        fallingPiece['y'] += 1
        lastFallTime = time.time()
 
    # drawing everything on the screen
    DISPLAYSURF.fill(BGCOLOR)
    drawBoard(board)
    drawStatus(score, level)
    drawNextPiece(nextPiece)
    if fallingPiece != None:
      drawPiece(fallingPiece)
 
    pygame.display.update()
    FPSCLOCK.tick(FPS)
 
 
def makeTextObjs(text, font, color):
  surf = font.render(text, True, color)
  return surf, surf.get_rect()
 
 
def terminate():
  pygame.quit()
  sys.exit()
 
 
def checkForKeyPress():
  # Go through event queue looking for a KEYUP event.
  # Grab KEYDOWN events to remove them from the event queue.
  checkForQuit()
 
  for event in pygame.event.get([KEYDOWN, KEYUP]):
    if event.type == KEYDOWN:
      continue
    return event.key
  return None
 
 
def showTextScreen(text):
  # This function displays large text in the
  # center of the screen until a key is pressed.
  # Draw the text drop shadow
  titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTSHADOWCOLOR)
  titleRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
  DISPLAYSURF.blit(titleSurf, titleRect)
 
  # Draw the text
  titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTCOLOR)
  titleRect.center = (int(WINDOWWIDTH / 2) - 3, int(WINDOWHEIGHT / 2) - 3)
  DISPLAYSURF.blit(titleSurf, titleRect)
 
  # Draw the additional "Press a key to play." text.
  pressKeySurf, pressKeyRect = makeTextObjs('Press a key to play.', BASICFONT, TEXTCOLOR)
  pressKeyRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 100)
  DISPLAYSURF.blit(pressKeySurf, pressKeyRect)
 
  while checkForKeyPress() == None:
    pygame.display.update()
    FPSCLOCK.tick()
 
 
def checkForQuit():
  for event in pygame.event.get(QUIT): # get all the QUIT events
    terminate() # terminate if any QUIT events are present
  for event in pygame.event.get(KEYUP): # get all the KEYUP events
    if event.key == K_ESCAPE:
      terminate() # terminate if the KEYUP event was for the Esc key
    pygame.event.post(event) # put the other KEYUP event objects back
 
 
def calculateLevelAndFallFreq(score):
  # Based on the score, return the level the player is on and
  # how many seconds pass until a falling piece falls one space.
  level = int(score / 10) + 1
  fallFreq = 0.27 - (level * 0.02)
  return level, fallFreq
 
def getNewPiece():
  # return a random new piece in a random rotation and color
  shape = random.choice(list(PIECES.keys()))
  newPiece = {'shape': shape,
        'rotation': random.randint(0, len(PIECES[shape]) - 1),
        'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2),
        'y': -2, # start it above the board (i.e. less than 0)
        'color': random.randint(0, len(COLORS)-1)}
  return newPiece
 
 
def addToBoard(board, piece):
  # fill in the board based on piece's location, shape, and rotation
  for x in range(TEMPLATEWIDTH):
    for y in range(TEMPLATEHEIGHT):
      if PIECES[piece['shape']][piece['rotation']][y][x] != BLANK:
        board[x + piece['x']][y + piece['y']] = piece['color']
 
 
def getBlankBoard():
  # create and return a new blank board data structure
  board = []
  for i in range(BOARDWIDTH):
    board.append([BLANK] * BOARDHEIGHT)
  return board
 
 
def isOnBoard(x, y):
  return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT
 
 
def isValidPosition(board, piece, adjX=0, adjY=0):
  # Return True if the piece is within the board and not colliding
  for x in range(TEMPLATEWIDTH):
    for y in range(TEMPLATEHEIGHT):
      isAboveBoard = y + piece['y'] + adjY < 0
      if isAboveBoard or PIECES[piece['shape']][piece['rotation']][y][x] == BLANK:
        continue
      if not isOnBoard(x + piece['x'] + adjX, y + piece['y'] + adjY):
        return False
      if board[x + piece['x'] + adjX][y + piece['y'] + adjY] != BLANK:
        return False
  return True
 
def isCompleteLine(board, y):
  # Return True if the line filled with boxes with no gaps.
  for x in range(BOARDWIDTH):
    if board[x][y] == BLANK:
      return False
  return True
 
 
def removeCompleteLines(board):
  # Remove any completed lines on the board, move everything above them down, and return the number of complete lines.
  numLinesRemoved = 0
  y = BOARDHEIGHT - 1 # start y at the bottom of the board
  while y >= 0:
    if isCompleteLine(board, y):
      # Remove the line and pull boxes down by one line.
      for pullDownY in range(y, 0, -1):
        for x in range(BOARDWIDTH):
          board[x][pullDownY] = board[x][pullDownY-1]
      # Set very top line to blank.
      for x in range(BOARDWIDTH):
        board[x][0] = BLANK
      numLinesRemoved += 1
      # Note on the next iteration of the loop, y is the same.
      # This is so that if the line that was pulled down is also
      # complete, it will be removed.
    else:
      y -= 1 # move on to check next row up
  return numLinesRemoved
 
 
def convertToPixelCoords(boxx, boxy):
  # Convert the given xy coordinates of the board to xy
  # coordinates of the location on the screen.
  return (XMARGIN + (boxx * BOXSIZE)), (TOPMARGIN + (boxy * BOXSIZE))
 
 
def drawBox(boxx, boxy, color, pixelx=None, pixely=None):
  # draw a single box (each tetromino piece has four boxes)
  # at xy coordinates on the board. Or, if pixelx & pixely
  # are specified, draw to the pixel coordinates stored in
  # pixelx & pixely (this is used for the "Next" piece).
  if color == BLANK:
    return
  if pixelx == None and pixely == None:
    pixelx, pixely = convertToPixelCoords(boxx, boxy)
  pygame.draw.rect(DISPLAYSURF, COLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 1, BOXSIZE - 1))
  pygame.draw.rect(DISPLAYSURF, LIGHTCOLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 4, BOXSIZE - 4))
 
 
def drawBoard(board):
  # draw the border around the board
  pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (XMARGIN - 3, TOPMARGIN - 7, (BOARDWIDTH * BOXSIZE) + 8, (BOARDHEIGHT * BOXSIZE) + 8), 5)
 
  # fill the background of the board
  pygame.draw.rect(DISPLAYSURF, BGCOLOR, (XMARGIN, TOPMARGIN, BOXSIZE * BOARDWIDTH, BOXSIZE * BOARDHEIGHT))
  # draw the individual boxes on the board
  for x in range(BOARDWIDTH):
    for y in range(BOARDHEIGHT):
      drawBox(x, y, board[x][y])
 
 
def drawStatus(score, level):
  # draw the score text
  scoreSurf = BASICFONT.render('Score: %s' % score, True, TEXTCOLOR)
  scoreRect = scoreSurf.get_rect()
  scoreRect.topleft = (WINDOWWIDTH - 150, 20)
  DISPLAYSURF.blit(scoreSurf, scoreRect)
 
  # draw the level text
  levelSurf = BASICFONT.render('Level: %s' % level, True, TEXTCOLOR)
  levelRect = levelSurf.get_rect()
  levelRect.topleft = (WINDOWWIDTH - 150, 50)
  DISPLAYSURF.blit(levelSurf, levelRect)
 
 
def drawPiece(piece, pixelx=None, pixely=None):
  shapeToDraw = PIECES[piece['shape']][piece['rotation']]
  if pixelx == None and pixely == None:
    # if pixelx & pixely hasn't been specified, use the location stored in the piece data structure
    pixelx, pixely = convertToPixelCoords(piece['x'], piece['y'])
 
  # draw each of the boxes that make up the piece
  for x in range(TEMPLATEWIDTH):
    for y in range(TEMPLATEHEIGHT):
      if shapeToDraw[y][x] != BLANK:
        drawBox(None, None, piece['color'], pixelx + (x * BOXSIZE), pixely + (y * BOXSIZE))
 
 
def drawNextPiece(piece):
  # draw the "next" text
  nextSurf = BASICFONT.render('Next:', True, TEXTCOLOR)
  nextRect = nextSurf.get_rect()
  nextRect.topleft = (WINDOWWIDTH - 120, 80)
  DISPLAYSURF.blit(nextSurf, nextRect)
  # draw the "next" piece
  drawPiece(piece, pixelx=WINDOWWIDTH-120, pixely=100)
 
 
if __name__ == '__main__':
  main()

代碼一開始仍是一些變量的初始化,我們這里還加載了time模塊,后面會用到。BOXSIZE, BOARDWIDTH, BOARDHEIGHT與前面貪吃蛇相關初始化類似,使其與屏幕像素點聯系起來。

?
1
2
MOVESIDEWAYSFREQ = 0.15
MOVEDOWNFREQ = 0.1

這兩個變量的作用是這樣的,每當游戲者按下左鍵或右鍵,下降的方塊相應的向左或右移一個格子。然而游戲者也可以一直按下方向左鍵或右鍵讓方塊保持移動。MOVESIDEWAYSFREQ這個固定值表示如果一直按下方向左鍵或右鍵那么每0.15秒方塊才會繼續移動。
MOVEDOWNFREQ 這個固定值與上面的是一樣的除了它是告訴當游戲者一直按下方向下鍵時方塊下落的頻率。

?
1
2
XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2)
TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5

這兩句的意思就看下面這個圖就明白了。

Python使用pygame模塊編寫俄羅斯方塊游戲的代碼實例

然后是一些顏色值的定義。其中要注意的是COLORS和LIGHTCOLORS,COLORS是組成方塊的小方塊的顏色,而LIGHTCOLORS是圍繞在小方塊周圍的顏色,為了強調出輪廓而設計的。
接著是定義方塊了。游戲必須知道每個類型的方塊有多少種形狀,在這里我們用在列表中嵌入含有字符串的列表來構成這個模版,一個方塊類型的模版含有了這個方塊可能變換的所有形狀。比如I的模版如下:

?
1
2
3
4
5
6
7
8
9
10
I_SHAPE_TEMPLATE = [['..O..',
           '..O..',
           '..O..',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           'OOOO.',
           '.....',
           '.....']]

TEMPLATEWIDTH = 5和TEMPLATEHEIGHT = 5則表示組成形狀的行和列,如下圖所示:

Python使用pygame模塊編寫俄羅斯方塊游戲的代碼實例

在看這段定義。

?
1
2
3
4
5
6
7
PIECES = {'S': S_SHAPE_TEMPLATE,
     'Z': Z_SHAPE_TEMPLATE,
     'J': J_SHAPE_TEMPLATE,
     'L': L_SHAPE_TEMPLATE,
     'I': I_SHAPE_TEMPLATE,
     'O': O_SHAPE_TEMPLATE,
     'T': T_SHAPE_TEMPLATE}

PIECES這個變量是一個字典,里面儲存了所有的不同模版。因為每個又有一個類型的方塊的所有變換形狀。那就意味著PIECES變量包含了每個類型的方塊和所有的的變換形狀。這就是存放我們游戲中用到的形狀的數據結構。(又加強了對字典的理解)
主函數main()
主函數的前部分主要是創建一些全局變量和在游戲開始之前顯示一個開始畫面。

?
1
2
3
4
5
6
7
8
9
while True: # game loop
  if random.randint(0, 1) == 0:
    pygame.mixer.music.load('tetrisb.mid')
  else:
    pygame.mixer.music.load('tetrisc.mid')
  pygame.mixer.music.play(-1, 0.0)
  runGame()
  pygame.mixer.music.stop()
  showTextScreen('Game Over')

上面這段代碼中runGame()是程序的核心部分。循環中首先簡單的隨機決定采用哪個背景音樂。然后調用runGame(),當游戲失敗,runGame()就會返回到main()函數,這時會停止背景音樂和顯示游戲失敗的畫面。
當游戲者按下一個鍵,showTextScreen()顯示游戲失敗的函數就會返回。游戲循環會再次開始然后繼續下一次游戲。
runGame()

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def runGame():
  # setup variables for the start of the game
  board = getBlankBoard()
  lastMoveDownTime = time.time()
  lastMoveSidewaysTime = time.time()
  lastFallTime = time.time()
  movingDown = False # note: there is no movingUp variable
  movingLeft = False
  movingRight = False
  score = 0
  level, fallFreq = calculateLevelAndFallFreq(score)
 
  fallingPiece = getNewPiece()
  nextPiece = getNewPiece()

在游戲開始和方塊掉落之前,我們需要初始化一些跟游戲開始相關的變量。fallingPiece變量被賦值成當前掉落的變量,nextPiece變量被賦值成游戲者可以在屏幕NEXT區域看見的下一個方塊。

?
1
2
3
4
5
6
7
8
9
10
11
while True: # game loop
  if fallingPiece == None:
    # No falling piece in play, so start a new piece at the top
    fallingPiece = nextPiece
    nextPiece = getNewPiece()
    lastFallTime = time.time() # reset lastFallTime
 
    if not isValidPosition(board, fallingPiece):
      return # can't fit a new piece on the board, so game over
 
  checkForQuit()

這部分包含了當方塊往底部掉落時的的所有代碼。fallingPiece變量在方塊著陸后被設置成None。這意味著nextPiece變量中的下一個方塊應該被賦值給fallingPiece變量,然后一個隨機的方塊又會被賦值給nextPiece變量。lastFallTime變量也被賦值成當前時間,這樣我們就可以通過fallFreq變量控制方塊下落的頻率。
來自getNewPiece函數的方塊只有一部分被放置在方框區域中。但是如果這是一個非法的位置,比如此時游戲方框已經被填滿(isVaildPostion()函數返回False),那么我們就知道方框已經滿了,游戲者輸掉了游戲。當這些發生時,runGame()函數就會返回。
事件處理循環
事件循環主要處理當翻轉方塊,移動方塊時或者暫停游戲時的一些事情。
暫停游戲

?
1
2
3
4
5
6
7
8
9
if (event.key == K_p):
  # Pausing the game
  DISPLAYSURF.fill(BGCOLOR)
  pygame.mixer.music.stop()
  showTextScreen('Paused') # pause until a key press
  pygame.mixer.music.play(-1, 0.0)
  lastFallTime = time.time()
  lastMoveDownTime = time.time()
  lastMoveSidewaysTime = time.time()

如果游戲者按下P鍵,游戲就會暫停。我們應該隱藏掉游戲界面以防止游戲者作弊(否則游戲者會看著畫面思考怎么處理方塊),用DISPLAYSURF.fill(BGCOLOR)就可以實現這個效果。注意的是我們還要保存一些時間變量值。

?
1
2
3
4
5
6
elif (event.key == K_LEFT or event.key == K_a):
  movingLeft = False
elif (event.key == K_RIGHT or event.key == K_d):
  movingRight = False
elif (event.key == K_DOWN or event.key == K_s):
  movingDown = False

停止按下方向鍵或ASD鍵會把moveLeft,moveRight,movingDown變量設置為False.,表明游戲者不再想要在此方向上移動方塊。后面的代碼會基于moving變量處理一些事情。注意的上方向鍵和W鍵是用來翻轉方塊的而不是移動方塊。這就是為什么沒有movingUp變量.

?
1
2
3
4
5
6
7
elif event.type == KEYDOWN:
  # moving the piece sideways
  if (event.key == K_LEFT or event.key == K_a) and isValidPosition(board, fallingPiece, adjX=-1):
    fallingPiece['x'] -= 1
    movingLeft = True
    movingRight = False
    lastMoveSidewaysTime = time.time()

當左方向鍵按下(而且往左移動是有效的,通過調用isVaildPosition()函數知道的),那么我們應該改變一個方塊的位置使其向左移動一個通過讓rallingPiece['x']減1.isVaildPosition()函數有個參數選項是adjX和adjY.平常,isVaildPostion()函數檢查方塊的位置通過函數的第二個參數的傳遞。然而,有時我們不想檢查方塊當前的位置,而是偏離當前方向幾個格子的位置。
比如adjX=-1,則表示向左移動一個格子后方塊的位置,為+1則表示向右移動一個格子后的位置。adjY同理如此。
movingLeft變量會被設置為True,確保方塊不會向右移動,此時movingRight變量設置為False。同時需要更新lastMoveSidewaysTime的值。
這個lastMoveSidewaysTime變量設置的原因是這樣。因為游戲者有可能一直按著方向鍵讓其方塊移動。如果moveLeft被設置為True,程序就會知道方向左鍵已經被按下。如果在lastMoveSidewaysTime變量儲存的時間基礎上,0.15秒(儲存在MOVESIDEAYSFREQ變量中)過去后,那么此時程序就會將方塊再次向左移動一個格子。

?
1
2
3
4
elif (event.key == K_UP or event.key == K_w):
          fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
          if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])

如果方向鍵上或W鍵被按下,那么就會翻轉方塊。上面的代碼做的就是將儲存在fallingPiece字典中的‘rotation'鍵的鍵值加1.然而,當增加的'rotation'鍵值大于所有當前類型方塊的形狀的數目的話(此變量儲存在len(SHAPES[fallingPiece['shape']])變量中),那么它翻轉到最初的形狀。
 

?
1
2
if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])

如果翻轉后的形狀無效因為其中的一些小方塊已經超過邊框的范圍,那么我們就要把它變回原來的形狀通過將fallingPiece['rotation')減去1.

?
1
2
3
4
elif (event.key == K_q): # rotate the other direction
          fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
          if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])

這段代碼與上面之前的那段代碼是一個意思,不同的是這段代碼是當游戲者按下Q鍵時翻轉方塊朝相反的方向。這里我們減去1而不是加1.

?
1
2
3
4
5
elif (event.key == K_DOWN or event.key == K_s):
  movingDown = True
  if isValidPosition(board, fallingPiece, adjY=1):
    fallingPiece['y'] += 1
  lastMoveDownTime = time.time()

如果下鍵被按下,游戲者此時希望方塊下降的比平常快。fallingPiece['y'] += 1使方塊下落一個格子(前提是這是一個有效的下落)moveDown被設置為True,lastMoceDownTime變量也被設置為當前時間。這個變量以后將被檢查當方向下鍵一直按下時從而保證方塊以一個比平常快的速率下降。

?
1
2
3
4
5
6
7
8
elif event.key == K_SPACE:
  movingDown = False
  movingLeft = False
  movingRight = False
  for i in range(1, BOARDHEIGHT):
    if not isValidPosition(board, fallingPiece, adjY=i):
      break
  fallingPiece['y'] += i - 1

當游戲者按下空格鍵,方塊將會迅速的下落至著陸。程序首先需要找出到它著陸需要下降個多少個格子。其中有關moving的三個變量都要被設置為False(保證程序后面部分的代碼知道游戲者已經停止了按下所有的方向鍵)。

?
1
2
3
4
5
6
if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ:
  if movingLeft and isValidPosition(board, fallingPiece, adjX=-1):
    fallingPiece['x'] -= 1
  elif movingRight and isValidPosition(board, fallingPiece, adjX=1):
    fallingPiece['x'] += 1
  lastMoveSidewaysTime = time.time()

這段代碼是處理一直按下某個方向鍵時的情況。
如果用戶按住鍵超過0.15秒。那么表達式(movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ:返回True。這樣的話我們就可以移動方塊向左或向右移動一個格子。
這個做法是很用的,因為如果用戶重復的按下方向鍵讓方塊移動多個格子是很煩人的。好的做法是,用戶可以按住方向鍵讓方塊保持移動直到松開鍵為止。最后別忘了更新lastMoveSideWaysTime變量。

?
1
2
3
if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1):
  fallingPiece['y'] += 1
  lastMoveDownTime = time.time()

這段代碼的意思跟上面的代碼差不多。

?
1
2
3
4
5
6
7
8
9
10
11
12
if time.time() - lastFallTime > fallFreq:
  # see if the piece has landed
  if not isValidPosition(board, fallingPiece, adjY=1):
    # falling piece has landed, set it on the board
    addToBoard(board, fallingPiece)
    score += removeCompleteLines(board)
    level, fallFreq = calculateLevelAndFallFreq(score)
    fallingPiece = None
  else:
    # piece did not land, just move the piece down
    fallingPiece['y'] += 1
    lastFallTime = time.time()

方塊自然下落的速率由lastFallTime變量決定。如果自從上個方塊掉落了一個格子后過去了足夠的時間,那么上面代碼就會再讓方塊移動一個格子。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 青青草久 | 久久国 | 亚洲成av人片在线观看无码 | 国产深夜视频在线观看 | 免费在线黄视频 | 成人精品国产免费网站 | 伊大人久久香线焦宗合一75大 | 91麻豆精品国产91久久久资源速度 | 精品成人18| 成人午夜精品一区二区三区 | 一区在线视频观看 | 日韩中文字幕在线播放 | 日韩有码在线观看 | 成人福利在线观看 | 亚洲视频第一页 | 国产成人av综合 | 一级做a爰性色毛片免费1 | 欧美区亚洲区 | 精品国产乱码久久久久久久 | 国产精品国产a | 国产乱码一区二区三区在线观看 | 亚洲成人精品一区 | 人人干人人看 | 国产高清精品一区 | 激情网五月天 | 爱逼色| 欧美二区三区视频 | 91嫩草视频在线 | 野花国产精品入口 | 亚洲欧美另类久久久精品2019 | 成人高清视频在线观看 | 国产美女视频网站 | 亚洲色图 偷拍自拍 | 国产91精品亚洲精品日韩已满 | 久久久免费电影 | 欧美精品日韩 | 欧美在线影院 | 久久综合久 | 正在播放国产一区 | 久久久www | 亚洲欧美一级久久精品 |