Pygame用200行代碼實(shí)現(xiàn)俄羅斯方塊
邏輯設(shè)計
俄羅斯方塊的邏輯很簡單,就是幾個方塊組合在一起,然后下落,當(dāng)其碰到四周的墻壁后便無法移動。若某行被方塊所填滿,那么就刪除這一行,然后此行上面的所有方塊下降一行。
為了將這個邏輯代碼化,可以用布爾矩陣來表示具體的方塊類型,比如長條形方塊可用如下矩陣表示。
BLOCK_I = [[0,1,0,0], [0,1,0,0], [0,1,0,0], [0,1,0,0]]
在實(shí)際操作時,會經(jīng)常遇到旋轉(zhuǎn)操作,其基本邏輯是,將第i ii行變成第− i -i−i列,從而旋轉(zhuǎn)邏輯如下
def rotateBlock(block): newBlock = [[[] for _ in block] for b in block[0]] for i,row in enumerate(block, 1): for j,r in enumerate(row, 0): newBlock[j][-i] = r return newBlock
定義好所有的方塊,并將其封入列表BLOCK
中,
BLOCK_S = [[0,1,1], [1,1,0], [0,0,0]] BLOCK_Z = [[1,1,0], [0,1,1], [0,0,0]] # I型方塊 BLOCK_I = [[0,1,0,0], [0,1,0,0], [0,1,0,0], [0,1,0,0]], # O型方塊 BLOCK_O = [[1,1], [1,1]] # J型方塊 BLOCK_J = [[1,0,0], [1,1,1], [0,0,0]] # L型方塊 BLOCK_L = [[0,0,1], [1,1,1], [0,0,0]] # T型方塊 BLOCK_T = [[0,1,0], [1,1,1], [0,0,0]] BLOCKS = [BLOCK_S, BLOCK_I, BLOCK_J, BLOCK_L, BLOCK_O, BLOCK_T, BLOCK_Z]
有了這個,就可以隨機(jī)生成其中某個方塊了,這個過程需要引入一點(diǎn)隨機(jī)性
import random # 創(chuàng)建一個圖形 def newBlock(): block = random.choice(BLOCKS) for _ in range(random.randint(0,4)): block = rotateBlock(block) return block
挪動邏輯
原理十分簡單,但想要寫出一個可以玩的游戲,卻還需要注意一些細(xì)節(jié)問題。比如俄羅斯方塊在碰到墻壁時的行為;旋轉(zhuǎn)方塊的方法。在實(shí)現(xiàn)這些具體問題之前,先設(shè)置幾個常量
SCREEN_WIDTH, SCREEN_HEIGHT = 450, 750 BLOCK_COL_NUM = 10 # 每行的方格數(shù) BLOCK_ROW_NUM = 25 # 每列的方個數(shù) SIZE = 30 # 每個小方格大小 BG_COLOR = (40, 40, 60) # 背景色 RED = (200, 30, 30) # 紅色,GAME OVER 的字體顏色 WHITE = (255,255,255) # 白色 BLACK = (0,0,0) # 黑色 GREEN = (60, 128, 80) # 綠色 STOP_LST = [[0 for _ in range(BLOCK_COL_NUM)] for _ in range(BLOCK_ROW_NUM)]
接下來逐個實(shí)現(xiàn)俄羅斯方塊的細(xì)節(jié),當(dāng)左右移動方塊時,若方塊碰到墻壁,則需要一個判定函數(shù),代碼如下。其中isR
為True時表示向右移動,否則向左移動。其判斷邏輯非常直觀,只需查看是否超出邊界。
# 判斷是否可以向左右移動 # isR為True,判斷能否向右移動;isR為False,判斷能否向左移動 def judgeMoveLR(block, stCol, isR): nCol = len(block[0]) cols = range(nCol-1, -1, -1) if isR else range(nCol) for col in cols: lstCol = [line[col] for line in block] if 1 not in lstCol: continue if not 0 <= stCol + col < BLOCK_COL_NUM: return False return True
如果判定成功,則需要移動方塊,那么根據(jù)其輸入左移或者右移,可以構(gòu)造一個函數(shù)
# 相左或者向右移動 def moveLR(block, stCol, key): isR = key == pygame.K_RIGHT stCol = stCol + 1 if isR else stCol - 1 if judgeMoveLR(block ,stCol, isR): return 1 if isR else -1 return 0
方塊在下落過程中若碰壁,則稍微有些麻煩,因?yàn)橄路娇赡芤呀?jīng)堆積了一些方塊,為此不僅需要知道當(dāng)前下落方塊的信息,還要知道已經(jīng)堆積的方塊的信息。
# 判斷是否可以繼續(xù)下落 def judgeMoveDown(block, stRow, stCol, stopLst): # 堆積方塊的位置 stopPosition = list() for row, line in enumerate(stopLst): for col, b in enumerate(line): if b: stopPosition.append((row, col)) # 判斷碰撞 for row, line in enumerate(block): if 1 in line and stRow + row >= BLOCK_ROW_NUM: return False for col, b in enumerate(line): if b and (stRow + row, stCol + col) in stopPosition: return False return True
最后將三者合在一起,用于判斷方塊是否可以繼續(xù)移動
def canMove(block, stRow, stCol, stopLst): flag = judgeMoveLR(block, stCol, False) flag &= judgeMoveLR(block, stCol, True) flag &= judgeMoveDown(block, stRow, stCol, stopLst) return flag
消除和堆積
其消除邏輯是,當(dāng)某行全為1的時候,就把這行刪掉,然后在頂端補(bǔ)充全0的列表。
def judgeLines(stopLst): # 記錄剛剛消除的行數(shù) i, N, count = 0, len(stopLst), 0 for i in range(N): if 0 not in stopLst[i]: line = [0]*len(stopLst.pop(i)) stopLst.insert(0, line) count += 10 return count
堆積邏輯則相對簡單,只需將堆積部位置為1即可。
# 將停止移動的block添加到堆積方塊 def addToStopLst(stopLst, block, stRow, stCol): for row, line in enumerate(block): for col, b in enumerate(line): if b: stopLst[stRow + row][stCol + col] = 1
至此,俄羅斯方塊的操作邏輯便設(shè)計完成,接下來是繪圖。
參數(shù)邏輯
俄羅斯方塊的下落速度可以區(qū)分不同關(guān)卡,下面是一種用分?jǐn)?shù)決定下落速度的方案,其速度值表示方塊下落的延時時間。
def changeSpeed(score): scores = [20, 50, 100, 200] infos = "12345" speeds = [0.5, 0.4, 0.3, 0.2, 0.1] ind = bisect(scores, score) return infos[ind], speeds[ind]
繪圖邏輯
首先是背景繪制,主要包括背景顏色和網(wǎng)格的繪制。
def drawBackground(screen): screen.fill(BG_COLOR) width = SIZE * BLOCK_COL_NUM pygame.draw.line(screen, BLACK, (width, 0), (width, SCREEN_HEIGHT), 4) for x in range(BLOCK_COL_NUM): pygame.draw.line(screen, BLACK, (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1) for y in range(BLOCK_ROW_NUM): pygame.draw.line(screen, BLACK, (0, y * SIZE), (width, y * SIZE), 1)
這里僅采使用了左側(cè)窗口,其右側(cè)將用于存放提示信息,主要包括得分、速度等級以及接下來落下的方塊。
def drawRight(screen, score, speedInfo): fontPath = r'C:\Windows\Fonts\msyh.ttc' font = pygame.font.Font(fontPath, 24) # 黑體24 posDct = { '得分: ':10, str(score): 50, '速度: ':100, speedInfo: 150, '下一個:':200 } for k,v in posDct.items(): msg = font.render(k, True, WHITE) screen.blit(msg, (BLOCK_COL_NUM * SIZE + 10, v))
然后是方塊繪制函數(shù),顧名思義,drawBlock繪制一個方塊,drawBlocks繪制一組方塊。
def drawBlock(screen, row, col, stRow, stCol): stRow += row * SIZE stCol += SIZE * col rect = pygame.Rect(stCol, stRow, SIZE, SIZE) pygame.draw.rect(screen, GREEN, rect, 0) pygame.draw.rect(screen, BLACK, rect, 1) def drawBlocks(screen, block, stRow, stCol): for row, line in enumerate(block): for col, b in enumerate(line): if not b: continue drawBlock(screen, row, col, stRow, stCol)
最后,若游戲失敗,則彈出失敗信息
def drawGamerOver(screen): fontPath = r'C:\Windows\Fonts\msyh.ttc' font = pygame.font.Font(fontPath, 72) w, h = font.size('GAME OVER') msg = font.render('GAME OVER', True, RED) screen.blit(msg, ((SCREEN_WIDTH - w) // 2, (SCREEN_HEIGHT - h) // 2)) # 顯示"鼠標(biāo)點(diǎn)擊任意位置,再來一局" font = pygame.font.Font(fontPath, 24) w, h = font.size('點(diǎn)擊任意位置,再來一局') msg = font.render('點(diǎn)擊任意位置,再來一局', True, RED) screen.blit(msg, ((SCREEN_WIDTH - w) // 2, (SCREEN_HEIGHT - h) // 2 + 80))
主程序
主程序內(nèi)容如下,其中initBlock用于初始化方塊。由于大家對俄羅斯方塊都很熟悉,且所有調(diào)用的子函數(shù)均已說明,所以就不再細(xì)說其流程了。
def initBlock(nextBlock=None): stRow, stCol = -2, 4 nowBlock = nextBlock if nextBlock else newBlock() nextBlock = newBlock() return stRow, stCol, nowBlock, nextBlock def main(): pygame.init() screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption('俄羅斯方塊') # 初始化當(dāng)前圖形的位置,當(dāng)前圖形,以及下一個圖形 stRow, stCol, nowBlock, nextBlock = initBlock() last_time = time.time() speed, speedInfo = 0.5, '1' score, gameOver = 0, False stopLst = deepcopy(STOP_LST) clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key in [pygame.K_LEFT, pygame.K_RIGHT]: stCol += moveLR(nowBlock, stCol, event.key) elif event.key == pygame.K_UP: rotBlock = rotateBlock(nowBlock) if canMove(rotBlock, stRow, stCol, stopLst): nowBlock = rotBlock elif event.key == pygame.K_DOWN: # 判斷是否可以向下移動,如果碰到底部或者其它的圖形就不能移動了 while judgeMoveDown(nowBlock, stRow + 1, stCol, stopLst): stRow += 1 if gameOver: stRow, stCol, nowBlock, nextBlock = initBlock() stopLst = deepcopy(STOP_LST) score = 0 gameOver = False # 判斷是否修改當(dāng)前圖形顯示的起始行 if not gameOver and time.time() - last_time > speed: last_time = time.time() # 可以向下移動的情形 if judgeMoveDown(nowBlock, stRow + 1, stCol, stopLst): stRow += 1 else: # 不可向下移動,則需新建一個block然后下落 addToStopLst(stopLst, nowBlock, stRow, stCol) score += judgeLines(stopLst) gameOver = 1 in stopLst[0] # 第一行中間有1則游戲結(jié)束 speedInfo, speed = changeSpeed(score) # 調(diào)整速度 stRow, stCol, nowBlock, nextBlock = initBlock(nextBlock) drawBackground(screen) drawRight(screen, score, speedInfo) drawBlocks(screen, nowBlock, stRow*SIZE, stCol*SIZE) # 當(dāng)前方塊 drawBlocks(screen, stopLst, 0, 0) # 堆積方塊 drawBlocks(screen, nextBlock, 300, 320) # 下一個方塊 # 顯示游戲結(jié)束畫面 if gameOver: drawGamerOver(screen) pygame.display.update() clock.tick(60) # 通過一定的延時,實(shí)現(xiàn)1秒鐘能夠循環(huán)60次
最終游戲效果如下
以上就是Pygame用200行代碼實(shí)現(xiàn)俄羅斯方塊的詳細(xì)內(nèi)容,更多關(guān)于Pygame俄羅斯方塊的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Scrapy基于scrapy_redis實(shí)現(xiàn)分布式爬蟲部署的示例
這篇文章主要介紹了Scrapy基于scrapy_redis實(shí)現(xiàn)分布式爬蟲部署的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09在tensorflow中設(shè)置保存checkpoint的最大數(shù)量實(shí)例
今天小編就為大家分享一篇在tensorflow中設(shè)置保存checkpoint的最大數(shù)量實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01Python pandas 重命名索引和列名稱的實(shí)現(xiàn)
本文主要介紹了Python pandas 重命名索引和列名稱的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07使用OpenCV實(shí)現(xiàn)圖像的透視變換功能
在計算機(jī)視覺領(lǐng)域,經(jīng)常需要對圖像進(jìn)行各種幾何變換,如旋轉(zhuǎn)、縮放和平移等,本文主要介紹了如何使用OpenCV實(shí)現(xiàn)圖像的透視變換,需要的可以參考下2024-11-11Python實(shí)現(xiàn)對照片中的人臉進(jìn)行顏值預(yù)測
今天給大家?guī)淼氖顷P(guān)于Python實(shí)戰(zhàn)的相關(guān)知識,文章圍繞如何用Python實(shí)現(xiàn)對照片中的人臉進(jìn)行顏值預(yù)測展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06