Pygame用200行代碼實現(xiàn)俄羅斯方塊
邏輯設計
俄羅斯方塊的邏輯很簡單,就是幾個方塊組合在一起,然后下落,當其碰到四周的墻壁后便無法移動。若某行被方塊所填滿,那么就刪除這一行,然后此行上面的所有方塊下降一行。
為了將這個邏輯代碼化,可以用布爾矩陣來表示具體的方塊類型,比如長條形方塊可用如下矩陣表示。
BLOCK_I = [[0,1,0,0],
[0,1,0,0],
[0,1,0,0],
[0,1,0,0]]
在實際操作時,會經(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]
有了這個,就可以隨機生成其中某個方塊了,這個過程需要引入一點隨機性
import random
# 創(chuàng)建一個圖形
def newBlock():
block = random.choice(BLOCKS)
for _ in range(random.randint(0,4)):
block = rotateBlock(block)
return block
挪動邏輯
原理十分簡單,但想要寫出一個可以玩的游戲,卻還需要注意一些細節(jié)問題。比如俄羅斯方塊在碰到墻壁時的行為;旋轉(zhuǎn)方塊的方法。在實現(xiàn)這些具體問題之前,先設置幾個常量
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)]
接下來逐個實現(xiàn)俄羅斯方塊的細節(jié),當左右移動方塊時,若方塊碰到墻壁,則需要一個判定函數(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
方塊在下落過程中若碰壁,則稍微有些麻煩,因為下方可能已經(jī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
消除和堆積
其消除邏輯是,當某行全為1的時候,就把這行刪掉,然后在頂端補充全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ù)邏輯
俄羅斯方塊的下落速度可以區(qū)分不同關卡,下面是一種用分數(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))
# 顯示"鼠標點擊任意位置,再來一局"
font = pygame.font.Font(fontPath, 24)
w, h = font.size('點擊任意位置,再來一局')
msg = font.render('點擊任意位置,再來一局', True, RED)
screen.blit(msg, ((SCREEN_WIDTH - w) // 2, (SCREEN_HEIGHT - h) // 2 + 80))
主程序
主程序內(nèi)容如下,其中initBlock用于初始化方塊。由于大家對俄羅斯方塊都很熟悉,且所有調(diào)用的子函數(shù)均已說明,所以就不再細說其流程了。
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('俄羅斯方塊')
# 初始化當前圖形的位置,當前圖形,以及下一個圖形
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
# 判斷是否修改當前圖形顯示的起始行
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) # 當前方塊
drawBlocks(screen, stopLst, 0, 0) # 堆積方塊
drawBlocks(screen, nextBlock, 300, 320) # 下一個方塊
# 顯示游戲結(jié)束畫面
if gameOver:
drawGamerOver(screen)
pygame.display.update()
clock.tick(60) # 通過一定的延時,實現(xiàn)1秒鐘能夠循環(huán)60次
最終游戲效果如下

以上就是Pygame用200行代碼實現(xiàn)俄羅斯方塊的詳細內(nèi)容,更多關于Pygame俄羅斯方塊的資料請關注腳本之家其它相關文章!
相關文章
Scrapy基于scrapy_redis實現(xiàn)分布式爬蟲部署的示例
這篇文章主要介紹了Scrapy基于scrapy_redis實現(xiàn)分布式爬蟲部署的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09
在tensorflow中設置保存checkpoint的最大數(shù)量實例
今天小編就為大家分享一篇在tensorflow中設置保存checkpoint的最大數(shù)量實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
Python pandas 重命名索引和列名稱的實現(xiàn)
本文主要介紹了Python pandas 重命名索引和列名稱的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07

