接着貪吃蛇的下面一章就是俄羅斯方塊,這也是我之前喜歡玩的遊戲之一。它的代碼量也比貪吃蛇多多了,有大約500行。不過整個思路很容易的。python
文章先介紹了關於俄羅斯方塊遊戲的幾個術語。數據結構
下面先把代碼敲一遍,試着瞭解做者意圖,體會俄羅斯方塊遊戲的製做過程。app
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 = ( 0, 0, 0) RED = (155, 0, 0) LIGHTRED = (175, 20, 20) GREEN = ( 0, 155, 0) LIGHTGREEN = ( 20, 175, 20) BLUE = ( 0, 0, 155) LIGHTBLUE = ( 20, 20, 175) YELLOW = (155, 155, 0) 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與前面貪吃蛇相關初始化相似,使其與屏幕像素點聯繫起來。less
MOVESIDEWAYSFREQ = 0.15
MOVEDOWNFREQ = 0.1
dom
這兩個變量的做用是這樣的,每當遊戲者按下左鍵或右鍵,降低的方塊相應的向左或右移一個格子。然而遊戲者也能夠一直按下方向左鍵或右鍵讓方塊保持移動。MOVESIDEWAYSFREQ這個固定值表示若是一直按下方向左鍵或右鍵那麼每0.15秒方塊纔會繼續移動。ide
MOVEDOWNFREQ 這個固定值與上面的是同樣的除了它是告訴當遊戲者一直按下方向下鍵時方塊下落的頻率。
函數
XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2)
TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5
oop
這兩句的意思就看下面這個圖就明白了。post
而後是一些顏色值的定義。其中要注意的是COLORS和LIGHTCOLORS,COLORS是組成方塊的小方塊的顏色,而LIGHTCOLORS是圍繞在小方塊周圍的顏色,爲了強調出輪廓而設計的。ui
接着是定義方塊了。遊戲必須知道每一個類型的方塊有多少種形狀,在這裏咱們用在列表中嵌入含有字符串的列表來構成這個模版,一個方塊類型的模版含有了這個方塊可能變換的全部形狀。好比I的模版以下:
I_SHAPE_TEMPLATE = [['..O..', '..O..', '..O..', '..O..', '.....'], ['.....', '.....', 'OOOO.', '.....', '.....']]
在看這段定義。
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}
主函數main()
主函數的前部分主要是建立一些全局變量和在遊戲開始以前顯示一個開始畫面。
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')
當遊戲者按下一個鍵,showTextScreen()顯示遊戲失敗的函數就會返回。遊戲循環會再次開始而後繼續下一次遊戲。
runGame()
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()
來自getNewPiece函數的方塊只有一部分被放置在方框區域中。可是若是這是一個非法的位置,好比此時遊戲方框已經被填滿(isVaildPostion()函數返回False),那麼咱們就知道方框已經滿了,遊戲者輸掉了遊戲。當這些發生時,runGame()函數就會返回。
事件處理循環
事件循環主要處理當翻轉方塊,移動方塊時或者暫停遊戲時的一些事情。
暫停遊戲
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)就能夠實現這個效果。注意的是咱們還要保存一些時間變量值。
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變量.
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變量中)過去後,那麼此時程序就會將方塊再次向左移動一個格子。
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']])變量中),那麼它翻轉到最初的形狀。
if not isValidPosition(board, fallingPiece): fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])若是翻轉後的形狀無效由於其中的一些小方塊已經超過邊框的範圍,那麼咱們就要把它變回原來的形狀經過將fallingPiece['rotation')減去1.
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']])
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變量也被設置爲當前時間。這個變量之後將被檢查當方向下鍵一直按下時從而保證方塊以一個比日常快的速率降低。
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(保證程序後面部分的代碼知道遊戲者已經中止了按下全部的方向鍵)。
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變量。
if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1): fallingPiece['y'] += 1 lastMoveDownTime = time.time()
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變量決定。若是自從上個方塊掉落了一個格子後過去了足夠的時間,那麼上面代碼就會再讓方塊移動一個格子。