1. 程式人生 > 程式設計 >pygame庫實現俄羅斯方塊小遊戲

pygame庫實現俄羅斯方塊小遊戲

本文例項為大家分享了pygame庫實現俄羅斯方塊小遊戲的具體程式碼,供大家參考,具體內容如下

import random,time,pygame,sys
from pygame.locals import *#導pygame內定義的一些常量
FPS=25#每秒傳輸幀數(重新整理率),此處一秒內在螢幕上連續投射出24張靜止畫面
WINDOWWIDTH=640#視窗寬
WINDOWHEIGHT=480#視窗高
BOXSIZE=20#遊戲框大小
BOARDWIDTH=10#遊戲框寬度
BOARDHEIGHT=20#遊戲框高度
BLANK='.'#定義方塊形狀模板時,填補空白處的字元

MOVESIDEWAYSFREQ=0.15#玩家一直按下左或右方向鍵,方塊仍是每0.15s才會移動一次
MOVEDOWNFREQ=0.1#玩家一直按下下方向鍵,方塊仍是每0.1s移動一次

XMARGIN=(WINDOWWIDTH-BOARDWIDTH*BOXSIZE)/2#遊戲介面的寬度
TOPMARGIN=WINDOWHEIGHT-(BOARDHEIGHT*BOXSIZE)-5#遊戲介面的高度

#定義RGB顏色變數
WHITE=(255,255,255)#設定字型顏色
BLACK=(0,0)#整個視窗背景為黑色
GRAY=(185,185,185)#字型陰影灰色
#以下的顏色分別被COLORS和LIGHTCOLORS所引用
RED=(155,0)
LIGHTRED=(175,20,20)#淺紅
GREEN=(0,155,0)
LIGHTGREEN=(20,175,20)#淺綠
BLUE=(0,155)
LIGHTBLUE=(20,175)#淺藍
YELLOW=(155,0)
LIGHTYELLOW=(175,20)#淺黃

BORDERCOLOR=BLUE#遊戲邊框顏色為藍色
BGCOLOR=BLACK#背景顏色為黑色
TEXTCOLOR=WHITE#字型為白色,被showTextScreen()引用,用來設定暫停時的"Pause"文字字型顏色
TEXTSHADOWCOLOR=GRAY#字型陰影為灰色,用來設定暫停時的"Pause"文字陰影字型顏色
COLORS=(BLUE,GREEN,RED,YELLOW)#組成方塊的小方塊顏色
LIGHTCOLORS=(LIGHTBLUE,LIGHTGREEN,LIGHTRED,LIGHTYELLOW)#圍繞在小方塊周邊顏色,強調輪廓

assert len(COLORS)==len(LIGHTCOLORS)#每個顏色對應其淺色

TEMPLATEWIDTH=5#模板寬
TEMPLATEHEIGHT=5#模板高

#定義方塊形狀模板(順時針旋轉變換),在列表中嵌入了含有字串的列表來構成這個模板,模板包含了這個方塊可能變換的所有形狀
S_SHAPE_TEMPLATE=[['.....','.....','..00.','.00..','.....'],['.....','..0..','...0.','.....']]
Z_SHAPE_TEMPLATE=[['.....','.0...','.....']]
I_SHAPE_TEMPLATE=[['..0..','0000.','.....']]
O_SHAPE_TEMPLATE=[['.....','.....']]
J_SHAPE_TEMPLATE=[['.....','.000.','.....']]
L_SHAPE_TEMPLATE=[['.....','.....']]
T_SHAPE_TEMPLATE=[['.....','.....']]
#定義字典變數PIECES來儲存所有的不同形狀模板(重點是所有),即字典變數PIECES包含了每個型別的方塊和所有的變換形狀
PIECES={'S':S_SHAPE_TEMPLATE,'Z':Z_SHAPE_TEMPLATE,'I':I_SHAPE_TEMPLATE,'O':O_SHAPE_TEMPLATE,'J':J_SHAPE_TEMPLATE,'L':L_SHAPE_TEMPLATE,'T':T_SHAPE_TEMPLATE}
#主函式用於建立一些全域性變數並在遊戲開始之前顯示一個初始畫面
def main():
  global FPSCLOCK,DISPLAYSURF,BASICFONT,BIGFONT
  pygame.init()#初始化pygame相關模組,為使用硬體做準備
  FPSCLOCK=pygame.time.Clock()#建立一個新物件,可以使用時鐘跟蹤的時間量。該時鐘還提供多種功能以幫助控制遊戲的幀率。返回一個Clock物件
  DISPLAYSURF=pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))#顯示視窗(返回一個Surface物件)
  BASICFONT=pygame.font.Font('freesansbold.ttf',18)#用來設定暫停時"Press a key to play."字型顏色,被showTextScreen()引用
  BIGFONT=pygame.font.Font('freesansbold.ttf',100)#用來設定暫停時"Pause"文字及其陰影文字字型大小,被showTextScreen()引用          
  pygame.display.set_caption('俄羅斯方塊')
  while True:
    #隨機播放背景音樂,mid音樂格式由MIDI繼承而來。MID檔案並不是一段錄製好的音樂,而是記錄
    #聲音的資訊,然後告訴音效卡如何再現音樂的一組指令,所以音樂播放的好壞因不同機器的音效卡而不同
    if random.randint(0,1)==0:#加入異常語句,沒有mid音樂也可直接執行
      try:
        pygame.mixer.music.load('QQ火拼泡泡龍復原BGM---無損[遊戲中].mid')
      except:
        pass
    else:
      try:
        pygame.mixer.music.load('蛇蛇爭霸.mid')
      except:
        pass
    try:
      pygame.mixer.music.play(-1,0.0)#引數依次為無限迴圈播放,從音樂開頭播放
    except:
      pass
    runGame()
    try:
      pygame.mixer.music.stop()
    except:
      pass
    showTextScreen('Game Over')

def runGame():
  """啟動運行遊戲函式"""
  board=getBlankBoard()#返回一個新的空白板資料結構
  lastMoveDownTime=time.time()
  lastMoveSidewaysTime=time.time()
  lastFallTime=time.time()
  #按下方向鍵會將以下三個變數設定為None,上移變數用於翻轉方塊,故沒有上移變數
  movingDown=False
  movingLeft=False
  movingRight=False
  score=0
  level,fallFreq=calculateLevelAndFallFreq(score)
  fallingPiece=getNewPiece()#當前掉落的方塊
  nextPiece=getNewPiece()#遊戲玩家可以在螢幕的NEXT區域看見的下一個方塊

  while True:#開始遊戲迴圈
    if fallingPiece==None:
      fallingPiece=nextPiece#沒有下降的一塊在運動,所以開始一個新的一塊在頂部,把nextPiece變數中的下一個方塊賦值給fallingPiece變數
      nextPiece=getNewPiece()
      lastFallTime=time.time()#重置lastFallTime變數,賦值為當前時間,這樣就可以通過變數fallFreq控制方塊下落頻率
      if not isValidPosition(board,fallingPiece):#不能在遊戲框中放下新的方塊,遊戲結束,Valid:有效的,Position:位置
        return#返回runGame函式呼叫處
    checkForQuit()#不斷檢查是否要退出
    for event in pygame.event.get():#事件處理迴圈
      if event.type==KEYUP:
        if event.key==K_p:#暫停遊戲
          DISPLAYSURF.fill(BGCOLOR)#將DISPLAYSURF(視窗Surface物件)重新填充為黑色
          showTextScreen('Paused')#DISPLAYSURF(視窗Surface物件)顯示暫停字樣
          lastMoveDownTime = time.time() 
          lastMoveSidewaysTime = time.time() 
          lastFallTime = 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:#將方塊側向移動
        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,adjX=1):
          fallingPiece['x']+=1
          movingRight=True
          movingLeft=False
          lastMoveSidewaysTime=time.time()
        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:#按q鍵,按相反方向旋轉
          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:#按下向下鍵,使得方塊下落得更快,fallingPiece['y']+=1使方塊下落一個格子,#前提是這是一個有效下落,把movingDown設定為True,把lastMoveDownTime變數
                             #設定為當前時間.當向下方向鍵一直pp按下時,以後將會檢查這個變數保證方塊以一個較快速率下降
          movingDown=True
          if isValidPosition(board,adjY=1):
            fallingPiece['y']+=1
          lastMoveDownTime=time.time()
        elif event.key==K_SPACE:#如果按下的是空格,則將方塊一步到底部。程式首先需要找出它著落需要下降多少個格子,有關moving的3個變數都要
                    #設定為False,用以保證程式後面部分程式碼知道遊戲玩家已經按下所有方向鍵。
          movingDown=False
          movingLeft=False
          movingRight=False
          for i in range(1,BOARDHEIGHT):
            if not isValidPosition(board,adjY=i):
              break
          fallingPiece['y']+=i-1
    if (movingLeft or movingRight) and (time.time()-lastMoveSidewaysTime) > MOVESIDEWAYSFREQ:#如果使用者按住按鍵超過0.15s,那麼該表示式返回True,便可使得方塊左或右移一個格子
                                               #因為如果使用者重複按下方向鍵讓方塊移動多個格子是很煩人的。好的做法便是使用者按下
                                               #方向鍵讓方塊保持移動,直到鬆開鍵為止
      if movingLeft and isValidPosition(board,adjX=-1):
        fallingPiece['x']-=1
      elif movingRight and isValidPosition(board,adjX=1):
        fallingPiece['x']+=1
      lastMoveSidewaysTime=time.time()
    if movingDown and time.time()-lastMoveDownTime>MOVEDOWNFREQ and isValidPosition(board,adjY=1):
      fallingPiece['y']+=1
      lastMoveDownTime=time.time()

    if time.time()-lastFallTime>fallFreq:#讓方塊自動落下如果它到了下落的時候
      #判斷方塊是否落地
      if not isValidPosition(board,adjY=1):#方塊已落地
        addToBoard(board,fallingPiece)#將落地的方塊新增到遊戲框中
        score+=removeCompleteLines(board)#判斷遊戲框中的每一行是否填滿,刪除填滿的一行,返回填滿數
        level,fallFreq=calculateLevelAndFallFreq(score)#計算遊戲等級以及下落頻率
        fallingPiece=None#將當前下落的方塊設定為空物件,然後在函式開頭重新賦值nextPiece
      else:#方塊沒有落地,只是把方塊移下來
        fallingPiece['y']+=1
        lastFallTime=time.time()
    #把所有東西畫在螢幕上
    DISPLAYSURF.fill(BGCOLOR)#設定視窗背景顏色
    drawBoard(board)#將空白資料模板繪畫到視窗
    drawStatus(score,level)#將狀態列繪畫到視窗
    drawNextPiece(nextPiece)#將下一個方塊顯示欄畫到視窗
    if fallingPiece != None:
      drawPiece(fallingPiece)
    pygame.display.update()
    FPSCLOCK.tick(FPS) 
        
            
        
        
def getBlankBoard():
  """建立並返回一個新的空白板資料結構,board列表為如下形式:
  [['.','.','.'],['.','.']]
  
  與BOARDWIDTH=10#遊戲框寬度,BOARDHEIGHT=20#遊戲框高度對應
  """
  board=[]
  for i in range(BOARDWIDTH):
    board.append([BLANK]*BOARDHEIGHT)
  return board
def calculateLevelAndFallFreq(score):
  """根據分數,返回玩家所處的關卡,以及掉落的棋子掉落到一個空格前的時間間隔。"""
  level=int(score/10)+1#等級
  fallFreq=0.27-(level*0.02)#下落頻率(frequence)
  return level,fallFreq
def getNewPiece():
  """以隨機旋轉和顏色返回隨機的新塊"""
  shape=random.choice(list(PIECES.keys()))#shape為S,Z,I....
  newPiece={'shape':shape,'rotation':random.randint(0,len(PIECES[shape])-1),#PIECES[shape]為S_SHAPE_TEMPLATE,Z_SHAPE_TEMPLATE,I_SHAPE_TEMPLATE.此處指一類方塊的可旋轉次數
       'x':int(BOARDWIDTH/2)-int(TEMPLATEWIDTH/2),'y':-2,'color':random.randint(0,len(COLORS)-1)}
  return newPiece
def checkForQuit():
  """檢查退出事件"""
  for event in pygame.event.get(QUIT):#獲取所有退出事件
    terminate()#如果存在任何退出事件,則呼叫終止函式
  for event in pygame.event.get(KEYUP):#獲取所有的放開鍵事件
    if event.key==K_ESCAPE:#如果KEYUP事件是針對Esc鍵的
      terminate()#終止
    pygame.event.post(event)#將其他KEYUP(非Esc鍵放開)事件物件放回事件佇列
    
def terminate():
  """退出函式"""
  pygame.quit()
  sys.exit()
def showTextScreen(text):
  """在視窗Surface物件的中央顯示大文字,直到按下一個鍵。用於繪製文字投影,注意:pygame沒有提供直接在現有
  表面繪製文字的方法:相反,您必須使用Font.render()來建立文字的影象(表面),然後將此影象blit到另一個表面。

  """
  #做出陰影效果
  titleSurf,titleRect=makeTextObjs(text,BIGFONT,TEXTSHADOWCOLOR)#BIGFONT為main函式中定義,TEXTSHADOWCOLOR為模組中定義,此處文字顏色TEXTSHADOWCOLOR(灰色),下面是TEXTCOLOR(白色)
  titleRect.center=(int(WINDOWWIDTH/2),int(WINDOWHEIGHT / 2))#設定矩形容器的位置,此處需要注意titleRect指的是每個文字Surface物件與其對應的全覆蓋矩形容器,此容器在這裡不可見
  DISPLAYSURF.blit(titleSurf,titleRect)#在視窗Surface物件畫文字Surface物件和與其對應的矩形Surface物件,做出blit這個動作的人是一個Surface類的例項
  #實體文字整體向左上移動3個單位,與上面的陰影效果唯一不同的就是傳入的顏色引數
  titleSurf,TEXTCOLOR)
  titleRect.center=(int(WINDOWWIDTH/2)-3,int(WINDOWHEIGHT/2)-3)
  DISPLAYSURF.blit(titleSurf,titleRect)
  #繪製"Paused"文字下附加的"Press a key to play."文字
  pressKeySurf,pressKeyRect=makeTextObjs('Press a key to play.',TEXTCOLOR)
  pressKeyRect.center=(int(WINDOWWIDTH/2),int(WINDOWHEIGHT/2)+100)
  DISPLAYSURF.blit(pressKeySurf,pressKeyRect)
  while checkForKeyPress()==None:
    pygame.display.update()
    FPSCLOCK.tick()#這種方法應該被呼叫一次。它將計算多少毫秒被呼叫。如果可選的幀率引數的函式以將延遲保持在低於給
            #定遊戲執行時每秒滴答。這有助於限制運行遊戲的速度。通過呼叫clock.tick每幀一次地(40),該程式不會執行超過40幀/秒。
def makeTextObjs(text,font,color):
  """這將建立一個新Surface物件,並在其上呈現指定的文字。"""
  surf=font.render(text,True,color)#引數依次為:要寫的文字,布林值(是否開啟抗鋸齒功能,True字型較為平滑,),字型顏色,背景色
  return surf,surf.get_rect()#返回一個覆蓋整個表面的新矩形(Rect物件,一個矩形的靈活容器)
def isValidPosition(board,piece,adjX=0,adjY=0):
  """如果塊在板內且沒有碰撞,則返回True,該函式我暫時不理解"""
  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 isOnBoard(x,y):
  return x>=0 and x<BOARDWIDTH and y<BOARDHEIGHT
def addToBoard(board,piece):
  """根據方塊的位置,形狀,旋轉填充的遊戲框中"""
  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 removeCompleteLines(board):
  """刪除遊戲框中所有已完成(Completed)的行,將上面的所有內容向下移動,並返回完整行數。"""
  numLinesRemoved=0#記錄刪除的行數
  y=BOARDHEIGHT-1#從遊戲框底部開始
  while y>=0:
    if isCompleteLine(board,y):#如果填充了這一行,則移開這條線,把所有方塊往下一拉
      for pullDownY in range(y,-1):
        for x in range(BOARDWIDTH):
          board[x][pullDownY]=board[x][pullDownY-1]
      for x in range(BOARDWIDTH):#將最上面一行設定為空白
        board[x][0]=BLANK
      numLinesRemoved+=1
    else:
      y-=1#繼續檢視下一行
  return numLinesRemoved
      
      
def isCompleteLine(board,y):
  """如果行中填充了沒有空格的框,則返回True"""
  for x in range(BOARDWIDTH):
    if board[x][y]==BLANK:
      return False
  return True
def calculateLevelAndFallFreq(score):
  """基於分數,返回玩家所在的關卡。"""
  level=int(score/10)+1
  fallFreq=0.27-(level*0.02)#根據level變數計算方塊每下落一個空間所需要的秒數
  return level,fallFreq

def drawBoard(board): 
 """將空白資料模板畫到視窗Surface物件上"""
 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):
  """在Surface視窗物件上畫在遊戲框旁邊的狀態列"""
  #繪製分數文字
  scoreSurf=BASICFONT.render("Score:%s"%score,TEXTCOLOR)#TEXTCOLOR為白色
  scoreRect=scoreSurf.get_rect()
  scoreRect.topleft = (WINDOWWIDTH - 150,20)
  DISPLAYSURF.blit(scoreSurf,scoreRect) 
  #繪製等級文字
  levelSurf = BASICFONT.render('Level: %s' % level,TEXTCOLOR)
  levelRect = levelSurf.get_rect()
  levelRect.topleft = (WINDOWWIDTH - 150,50)
  DISPLAYSURF.blit(levelSurf,levelRect)
def drawNextPiece(piece):
  #繪製"下一個"文字
  nextSurf=BASICFONT.render("Next:",TEXTCOLOR)
  nextRect=nextSurf.get_rect()
  nextRect.topleft=(WINDOWWIDTH-120,80)
  DISPLAYSURF.blit(nextSurf,nextRect)
  #繪製下一個"方塊",pixel中文為畫素
  drawPiece(piece,pixelx=WINDOWWIDTH-120,pixely=100)
def drawPiece(piece,pixelx=None,pixely=None):
  shapeToDraw=PIECES[piece['shape']][piece['rotation']]
  if pixelx==None and pixely==None:
    pixelx,pixely=convertToPixelCoords(piece['x'],piece['y'])#如果沒有指定pixelx & pixely,則使用儲存在片段資料結構中的位置
  #畫出組成這個方塊的每一個小方塊
  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 convertToPixelCoords(boxx,boxy):
  """將board上給定的xy座標轉換為螢幕上位置的xy座標,convert:轉換,pixel:畫素,coords:座標"""
  return (XMARGIN+(boxx*BOXSIZE)),(TOPMARGIN+(boxy * BOXSIZE))

def drawBox(boxx,boxy,color,pixely=None):
  """在黑板上的xy座標處畫一個盒子(每個四重奏有四個盒子)。或者,如果指定了pixelx & pixely,則繪製到儲存在pixelx & pixely中的畫素座標(這用於“下一個”塊)"""
  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],BOXSIZE - 4,BOXSIZE - 4))

def checkForKeyPress():
  """遍歷事件佇列,尋找KEYUP事件,獲取KEYDOWN事件,將它們從事件佇列中刪除"""
  checkForQuit()
  for event in pygame.event.get([KEYDOWN,KEYUP]):
    if event.type!=KEYDOWN:
      continue
    return event.key
  return None
if __name__=='__main__':
  main()

執行結果:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。