【Python】用Python寫一個俄羅斯方塊玩玩
- 一、引言
- 1.成品效果展示
- 二、思考準備
- 1.思考設計
- 2.代碼設計
- 2.1 游戲頁面
- 2.2 控件設計
- 2.2.1 方塊生成
- 2.2.2 方塊碰撞
- 2.2.3 方塊消融
- 2.2.4 游戲主循環
- 2.2.5 游戲窗口
- 三、游戲完整版
一、引言
- 今日看到侄子在玩游戲,湊近一看,原來是俄羅斯方塊。熟悉又懷念,童年的記憶瞬間涌上心頭。小時候覺得這游戲老厲害了,現在想想,好像就是數組的組合和消融,便想著自己寫一個試試。說干就干,沖!
1.成品效果展示
俄羅斯方塊實現過程
二、思考準備
1.思考設計
- 俄羅斯方塊作為風靡一時的游戲,由俄羅斯人阿列克謝·帕基特諾夫于1984年6月發明的休閑游戲,主要是通過方塊的組合、消融完成的;
- 所以得先設計有哪些方塊、方塊如何組合、完成一行時方塊消融并下降一行、方塊組合還得回旋轉,單次90°等等,考慮中ing…
2.代碼設計
2.1 游戲頁面
- 游戲首先得有個操控頁面,所以得設計一個
# 設計游戲窗口尺寸
SCREEN_WIDTH, SCREEN_HEIGHT = 300, 600
# 方塊大小
GRID_SIZE = 30
# 各種圖形顏色 可自定義調整
COLORS = [ # 顏色配置(含背景色+7種方塊色)(0, 0, 0), # 0: 黑色背景(255, 0, 0), # 1: 紅色-I型(0, 255, 0), # 2: 綠色-T型(0, 0, 255), # 3: 藍色-J型(255, 165, 0), # 4: 橙色-L型(255, 255, 0), # 5: 黃色-O型(128, 0, 128), # 6: 紫色-S型(0, 255, 255) # 7: 青色-Z型
]
2.2 控件設計
2.2.1 方塊生成
- 游戲開始最上方會有方塊掉落,隨機形狀和顏色
def new_piece(self):"""生成新方塊,隨機形狀和顏色"""shape = random.choice(SHAPES)return {'shape': shape, # 方塊形狀矩陣'x': (SCREEN_WIDTH//GRID_SIZE - len(shape[0])) // 2, # 初始水平居中'y': 0, # 初始垂直位置'color': random.randint(1, len(COLORS)-1) # 隨機顏色(排除背景色)}
2.2.2 方塊碰撞
- 方塊會一直下降,碰撞,下降時還要符合可以多次旋轉
def check_collision(self, dx=0, dy=0, rotate=False):"""碰撞檢測函數:param dx: 水平移動偏移量:param dy: 垂直移動偏移量:param rotate: 是否正在旋轉:return: 是否發生碰撞"""shape = self.current_piece['shape']# 旋轉時生成臨時形狀if rotate: shape = [list(row[::-1]) for row in zip(*shape)] # 矩陣旋轉算法:先轉置再反轉每行(順時針90度)for y, row in enumerate(shape):for x, cell in enumerate(row):if cell: # 僅檢測實體方塊new_x = self.current_piece['x'] + x + dxnew_y = self.current_piece['y'] + y + dy# 邊界檢測(左右越界/觸底/與其他方塊重疊)if not (0 <= new_x < len(self.grid[0])) or new_y >= len(self.grid):return Trueif new_y >=0 and self.grid[new_y][new_x]:return Truereturn False
2.2.3 方塊消融
- 方塊接觸時,合適的會消融、不合適的會疊加,消融了計算分數,消融的還要剔除
def merge_piece(self):"""將當前方塊合并到游戲網格,并觸發消行檢測"""for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:# 將方塊顏色寫入網格對應位置self.grid[self.current_piece['y']+y][self.current_piece['x']+x] = self.current_piece['color']# 消行并更新分數lines = self.clear_lines()self.score += lines * 100def clear_lines(self):"""消除滿行并返回消除行數"""lines = 0# 從底部向上掃描for i, row in enumerate(self.grid):if all(cell !=0 for cell in row): # 檢測整行填滿del self.grid[i] # 刪除該行self.grid.insert(0, [0]*len(row)) # 在頂部插入新空行lines +=1return lines
2.2.4 游戲主循環
- 游戲主體邏輯寫入,方塊的處理時間、操作事項和刷新等
def run(self):"""游戲主循環"""fall_time = 0 # 下落時間累計器while True:self.screen.fill(COLORS[0]) # 用背景色清屏# 計時系統(控制自動下落速度)fall_time += self.clock.get_rawtime()self.clock.tick() # 保持幀率穩定# 自動下落邏輯(每800ms下落一格)if fall_time >= 800:if not self.check_collision(dy=1):self.current_piece['y'] +=1 # 正常下落else:self.merge_piece() # 觸底合并self.current_piece = self.new_piece() # 生成新方塊# 游戲結束檢測(新方塊無法放置)if self.check_collision():print("Game Over 你完蛋啦! Score:", self.score)returnfall_time =0 # 重置計時器# 事件處理(適配Mac鍵盤布局)for event in pygame.event.get():if event.type == QUIT:pygame.quit()returnif event.type == KEYDOWN:# 左右移動(帶碰撞檢測)if event.key == K_LEFT and not self.check_collision(dx=-1):self.current_piece['x'] -=1elif event.key == K_RIGHT and not self.check_collision(dx=1):self.current_piece['x'] +=1# 軟下落(手動加速)elif event.key == K_DOWN:if not self.check_collision(dy=1):self.current_piece['y'] +=1# 旋轉方塊(帶碰撞檢測)elif event.key == K_UP and not self.check_collision(rotate=True):self.current_piece['shape'] = [list(row[::-1]) for row in zip(*self.current_piece['shape'])]# 硬下落(空格鍵一鍵到底) elif event.key == K_SPACE: while not self.check_collision(dy=1):self.current_piece['y'] +=1# 繪制邏輯,游戲網格for y, row in enumerate(self.grid):for x, color in enumerate(row):if color:# 繪制已落下方塊(留1像素間隙)pygame.draw.rect(self.screen, COLORS[color], (x*GRID_SIZE, y*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))# 繪制當前操作方塊for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:pygame.draw.rect(self.screen, COLORS[self.current_piece['color']],((self.current_piece['x']+x)*GRID_SIZE, (self.current_piece['y']+y)*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))# 刷新畫面pygame.display.flip()
2.2.5 游戲窗口
- 最后,游戲設計好了,必須有個游戲窗口來展示
def __init__(self):# 初始化游戲窗口self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))pygame.display.set_caption("Mac M1俄羅斯方塊")self.clock = pygame.time.Clock() # 游戲時鐘控制幀率# 游戲狀態初始化self.grid = [[0]*(SCREEN_WIDTH//GRID_SIZE) for _ in range(SCREEN_HEIGHT//GRID_SIZE)] # 20x10游戲網格self.current_piece = self.new_piece() # 當前操作方塊self.score = 0
三、游戲完整版
# 俄羅斯方塊設計
# -*- coding: utf-8 -*-
"""
功能:俄羅斯方塊,童年的回憶
作者:看海的四叔
最后更新:2025-04-16
"""import pygame
import random
from pygame.locals import *# 初始化配置
pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 300, 600
GRID_SIZE = 30
COLORS = [ (0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 165, 0), (255, 255, 0), (128, 0, 128), (0, 255, 255)
]SHAPES = [[[1,1,1,1]], [[1,1],[1,1]], [[0,1,0], [1,1,1]], [[1,1,1], [1,0,0]], [[1,1,1], [0,0,1]], [[0,1,1], [1,1,0]], [[1,1,0], [0,1,1]]
]class Tetris:"""游戲主控制類,處理游戲邏輯與渲染"""def __init__(self):self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))pygame.display.set_caption("Mac M1俄羅斯方塊")self.clock = pygame.time.Clock() self.grid = [[0]*(SCREEN_WIDTH//GRID_SIZE) for _ in range(SCREEN_HEIGHT//GRID_SIZE)] self.current_piece = self.new_piece() self.score = 0 def new_piece(self):"""生成新方塊(隨機形狀和顏色)"""shape = random.choice(SHAPES)return {'shape': shape, 'x': (SCREEN_WIDTH//GRID_SIZE - len(shape[0])) // 2, 'y': 0, 'color': random.randint(1, len(COLORS)-1) }def check_collision(self, dx=0, dy=0, rotate=False):"""碰撞檢測函數:param dx: 水平移動偏移量:param dy: 垂直移動偏移量:param rotate: 是否正在旋轉:return: 是否發生碰撞"""shape = self.current_piece['shape']if rotate: shape = [list(row[::-1]) for row in zip(*shape)] for y, row in enumerate(shape):for x, cell in enumerate(row):if cell: new_x = self.current_piece['x'] + x + dxnew_y = self.current_piece['y'] + y + dyif not (0 <= new_x < len(self.grid[0])) or new_y >= len(self.grid):return Trueif new_y >=0 and self.grid[new_y][new_x]:return Truereturn Falsedef merge_piece(self):"""將當前方塊合并到游戲網格,并觸發消行檢測"""for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:self.grid[self.current_piece['y']+y][self.current_piece['x']+x] = self.current_piece['color']lines = self.clear_lines()self.score += lines * 100def clear_lines(self):"""消除滿行并返回消除行數"""lines = 0for i, row in enumerate(self.grid):if all(cell !=0 for cell in row): del self.grid[i] self.grid.insert(0, [0]*len(row)) lines +=1return linesdef run(self):"""游戲主循環"""fall_time = 0 while True:self.screen.fill(COLORS[0]) fall_time += self.clock.get_rawtime()self.clock.tick() if fall_time >= 800:if not self.check_collision(dy=1):self.current_piece['y'] +=1 # 正常下落else:self.merge_piece() self.current_piece = self.new_piece() # 游戲結束檢測(新方塊無法放置)if self.check_collision():print("Game Over 你完蛋啦! Score:", self.score)returnfall_time =0 # 重置計時器for event in pygame.event.get():if event.type == QUIT:pygame.quit()returnif event.type == KEYDOWN:if event.key == K_LEFT and not self.check_collision(dx=-1):self.current_piece['x'] -=1elif event.key == K_RIGHT and not self.check_collision(dx=1):self.current_piece['x'] +=1elif event.key == K_DOWN:if not self.check_collision(dy=1):self.current_piece['y'] +=1elif event.key == K_UP and not self.check_collision(rotate=True):self.current_piece['shape'] = [list(row[::-1]) for row in zip(*self.current_piece['shape'])]elif event.key == K_SPACE: while not self.check_collision(dy=1):self.current_piece['y'] +=1for y, row in enumerate(self.grid):for x, color in enumerate(row):if color:pygame.draw.rect(self.screen, COLORS[color], (x*GRID_SIZE, y*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))for y, row in enumerate(self.current_piece['shape']):for x, cell in enumerate(row):if cell:pygame.draw.rect(self.screen, COLORS[self.current_piece['color']],((self.current_piece['x']+x)*GRID_SIZE, (self.current_piece['y']+y)*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))pygame.display.flip() if __name__ == "__main__":game = Tetris()game.run()