Python制作俄罗斯方块游戏代码【完整代码】
1. 导入库与初始化
import pygameimport randomimport sys# 初始化Pygamepygame.init()
这段代码首先导入了必要的库:pygame
用于游戏开发,random
用于随机生成方块,sys
用于系统操作。然后通过pygame.init()
初始化Pygame库,启动其所有模块,为后续后续游戏开发做好准备。
2. 游戏配置参数
# 配置参数CELL_SIZE = 30 # 每个网格的像素大小COLS, ROWS = 10, 20 # 游戏区域的列数和行数SIDEBAR_WIDTH = 150 # 侧边栏宽度WINDOW_WIDTH, WINDOW_HEIGHT = CELL_SIZE * COLS + SIDEBAR_WIDTH, CELL_SIZE * ROWSFPS = 30
这里定义了游戏的核心配置参数:
CELL_SIZE
:每个网格的像素大小,决定了方块的视觉尺寸COLS, ROWS
:游戏区域的列数和行数,采用标准俄罗斯方块的10×20布局SIDEBAR_WIDTH
:右侧边栏宽度,用于显示分数和游戏信息WINDOW_WIDTH, WINDOW_HEIGHT
:计算窗口总尺寸(游戏区+侧边栏)FPS
:游戏帧率,控制画面刷新速度
3. 颜色定义
# 定义颜色BLACK = (0, 0, 0)GRAY = (128, 128, 128)CYAN = (0, 255, 255)YELLOW = (255, 255, 0)MAGENTA = (255, 0, 255)GREEN = (0, 255, 0)RED = (255, 0, 0)BLUE = (0, 0, 255)ORANGE = (255, 165, 0)WHITE = (255, 255, 255)SIDEBAR_COLOR = (50, 50, 50)HIGHLIGHT_COLOR = (100, 100, 255)
定义了游戏中使用的所有颜色,采用RGB值表示:
- 基础色:黑、白、灰用于网格线
- 方块色:为7种不同形状的方块块分配了独特颜色(青色、黄色、洋红色等)
- 界面色:侧边栏背景色和高亮色,用于区分不同UI区域
4. 方块形状定义
SHAPES_COLORS = [ ([[1, 1, 1, 1]], CYAN), # I形 ([[1, 1], [1, 1]], YELLOW), # O形 ([[0, 1, 0], [1, 1, 1]], MAGENTA), # T形 ([[1, 1, 0], [0, 1, 1]], GREEN), # S形 ([[0, 1, 1], [1, 1, 0]], RED), # Z形 ([[1, 0, 0], [1, 1, 1]], BLUE), # J形 ([[0, 0, 1], [1, 1, 1]], ORANGE) # L形]
这是俄罗斯方块的核心定义,每个元素是一个元组,包含:
- 形状矩阵:用二维列表表示,1表示方块实体,0表示空白
- 对应颜色:每个形状状有固定颜色,符合传统俄罗斯方块的配色方案
共定义了7种经典俄罗斯方块形状:I形、O形、T形、S形、Z形、J形和L形。
5. 创建游戏窗口
# 创建窗口screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))pygame.display.set_caption(\"俄罗斯方块 - Pygame\")clock = pygame.time.Clock()
pygame.display.set_mode()
创建游戏窗口,尺寸使用之前计算的WINDOW_WIDTH
和WINDOW_HEIGHT
pygame.display.set_caption()
设置窗口标题clock = pygame.time.Clock()
创建时钟对象,用于控制游戏帧率
6. 字体初始化
# 初始化中文字体try: # 尝试使用系统中文字体 font_title = pygame.font.SysFont(\"simhei\", 28) # 标题字体 font_large = pygame.font.SysFont(\"simhei\", 24) # 大字体 font_medium = pygame.font.SysFont(\"simhei\", 20) # 中等字体 font_small = pygame.font.SysFont(\"simhei\", 18) # 小字体except: # 如果找不到中文字体,使用默认字体 font_title = pygame.font.Font(None, 28) font_large = pygame.font.Font(None, 24) font_medium = pygame.font.Font(None, 20) font_small = pygame.font.Font(None, 18)
这段代码处理中文显示问题:
- 尝试加载系统中的\"simhei\"(黑体)字体,创建不同大小的字体对象
- 如果加载失败(如系统中没有该字体),则使用Pygame默认字体
- 定义了四种不同大小的字体,分别用于标题、分数、操作说明等不同UI元素
7. 游戏网格初始化
# 游戏网格初始化grid = [[0] * COLS for _ in range(ROWS)]
创建一个ROWS×COLS
的二维列表作为游戏主网格:
- 0表示该位置为空
- 后续会用颜色值填充,表示该位置被方块占据
- 这个网格记录了所有已落下并固定的方块位置
8. 绘制网格函数
def draw_grid(surface): \"\"\"绘制当前网格状态\"\"\" for r in range(ROWS): for c in range(COLS): val = grid[r][c] rect = pygame.Rect(c * CELL_SIZE, r * CELL_SIZE, CELL_SIZE, CELL_SIZE) if val == 0: pygame.draw.rect(surface, BLACK, rect) pygame.draw.rect(surface, GRAY, rect, width=1) else: pygame.draw.rect(surface, val, rect)
draw_grid()
函数负责绘制游戏区域的网格:
- 遍历网格中的每个单元格
- 计算每个单元格在屏幕上的位置(
c * CELL_SIZE
,r * CELL_SIZE
) - 如果单元格为空(值为0),绘制黑色背景和灰色边框
- 如果单元格被方块占据(值为颜色),则用对应颜色填充
9. 绘制当前方块函数
def draw_piece(surface, piece): \"\"\"绘制当前下落的方块\"\"\" shape, color = piece.shape, piece.color for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = (piece.col + c) * CELL_SIZE y = (piece.row + r) * CELL_SIZE rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) pygame.draw.rect(surface, color, rect) pygame.draw.rect(surface, GRAY, rect, width=1)
draw_piece()
函数绘制正在下落的方块:
- 接收方块对象,获取其形状、颜色和位置信息
- 遍历方块形状矩阵,只绘制值为1的部分(实际方块)
- 计算每个方块单元在屏幕上的位置(基于方块的基准位置
(piece.row, piece.col)
) - 绘制彩色方块并添加灰色边框,增强视觉效果
10. 绘制侧边栏函数
def draw_sidebar(surface, score, next_piece): \"\"\"绘制侧边栏\"\"\" # 绘制侧边栏背景 sidebar_rect = pygame.Rect(CELL_SIZE * COLS, 0, SIDEBAR_WIDTH, WINDOW_HEIGHT) pygame.draw.rect(surface, SIDEBAR_COLOR, sidebar_rect) # 绘制分隔线 pygame.draw.line(surface, GRAY, (CELL_SIZE * COLS, 0), (CELL_SIZE * COLS, WINDOW_HEIGHT), 2) # 绘制标题 title_text = font_title.render(\"俄罗斯方块\", True, HIGHLIGHT_COLOR) title_rect = title_text.get_rect(center=(CELL_SIZE * COLS + SIDEBAR_WIDTH // 2, 25)) surface.blit(title_text, title_rect) # 绘制分数区域(省略部分代码) # 绘制下一个方块区域(省略部分代码) # 绘制操作说明区域(省略部分代码) # 绘制游戏提示(省略部分代码)
draw_sidebar()
函数绘制右侧信息面板:
- 首先绘制侧边栏背景和与游戏区的分隔线
- 包含多个功能区域:标题、分数显示、下一个方块预览、操作说明和游戏提示
- 使用不同大小的字体和布局,使信息层次清晰
- 通过
surface.blit()
方法将文本和图形绘制到屏幕上
11. 绘制下一个方块预览
def draw_next_piece(surface, piece, start_y): \"\"\"绘制下一个方块预览\"\"\" shape, color = piece.shape, piece.color # 计算预览区域中心位置 preview_x = CELL_SIZE * COLS + SIDEBAR_WIDTH // 2 - len(shape[0]) * CELL_SIZE // 2 for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = preview_x + c * CELL_SIZE y = start_y + r * CELL_SIZE rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) pygame.draw.rect(surface, color, rect) pygame.draw.rect(surface, GRAY, rect, width=1)
这个函数专门用于在侧边栏绘制下一个要出现的方块预览:
- 计算预览区域的中心位置,使方块居中显示
- 遍历方块形状矩阵,绘制每个实体部分
- 与主游戏区的方块绘制逻辑类似,但位置固定在侧边栏内
12. 碰撞检测函数
def check_collision(piece, test_shape=None): \"\"\"检测方块是否与边界或已有方块发生碰撞\"\"\" shape = test_shape if test_shape is not None else piece.shape for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = piece.col + c y = piece.row + r if x < 0 or x >= COLS or y >= ROWS: return True if y >= 0 and grid[y][x]: return True return False
碰撞检测是俄罗斯方块的核心逻辑之一:
- 检查方块是否与游戏边界(左、右、下)碰撞
- 检查方块是否与已落在网格中的方块碰撞
test_shape
参数用于旋转检测(先测试旋转后的形状是否会碰撞)- 遍历方块的每个实体部分,只要有一个部分碰撞就返回True
13. 方块合并到网格函数
def merge_piece_to_grid(piece): \"\"\"将当前方块合并到游戏网格中\"\"\" shape, color = piece.shape, piece.color for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = piece.col + c y = piece.row + r if 0 <= y < ROWS and 0 <= x < COLS: grid[y][x] = color
当方块无法继续下落时(已落地),需要将其合并到主网格中:
- 遍历方块的每个实体部分
- 将方块的颜色值写入到网格的对应位置
- 后续绘制网格时会显示这些固定的方块
14. 方块旋转函数
def rotate_matrix(matrix): \"\"\"旋转矩阵90度(顺时针)\"\"\" # 先转置矩阵,然后水平翻转 return [list(row) for row in zip(*matrix[::-1])]
这个函数实现矩阵的90度顺时针旋转,用于方块旋转:
matrix[::-1]
将矩阵上下翻转zip(*matrix)
实现矩阵转置- 最终将结果转换为列表,得到旋转后的形状矩阵
15. 消除满行函数
def clear_lines(): \"\"\"清除满行并返回清除的行数\"\"\" lines_cleared = 0 # 从底部开始检查每一行 for r in range(ROWS - 1, -1, -1): # 检查当前行是否已满 if all(grid[r][c] != 0 for c in range(COLS)): # 删除这一行 del grid[r] # 在顶部添加一个空行 grid.insert(0, [0] * COLS) lines_cleared += 1 # 因为删除了一行,所以需要重新检查当前行 r += 1 return lines_cleared
消除满行是俄罗斯方块的核心玩法:
- 从底部向上检查每一行是否被填满(
all(grid[r][c] != 0)
) - 若某行已满,删除该行并在顶部添加一个空行
- 每消除一行,计数器加1,并调整循环索引(因为删除行后行数变化)
- 返回消除的总行数,用于计算分数
16. 分数计算函数
def calculate_score(lines_cleared): \"\"\"根据消除行数计算分数\"\"\" if lines_cleared == 1: return 100 elif lines_cleared == 2: return 300 elif lines_cleared == 3: return 500 elif lines_cleared == 4: return 800 return 0
根据消除的行数计算得分,采用经典的非线性计分方式:
- 消除1行:100分
- 消除2行:300分(多于1行的2倍)
- 消除3行:500分
- 消除4行(Tetris):800分
这种计分方式鼓励玩家一次消除更多行。
17. 速度计算函数
def calculate_speed(score): \"\"\"根据分数计算下落速度\"\"\" # 基础速度为500毫秒,每1000分减少50毫秒,最低100毫秒 speed = max(500 - (score // 1000) * 50, 100) return speed
随着分数增加,方块下落速度会加快,增加游戏难度:
- 基础速度为500毫秒(每500ms下落一格)
- 每得1000分,速度增加50ms(下落更快)
- 最低速度限制为100ms,防止速度过快无法操作
18. 硬降函数
def hard_drop(piece): \"\"\"硬降:方块直接落到最底部\"\"\" while piece.move(dr=1): pass
实现\"硬降\"功能,让方块直接落到当前位置的最底部:
- 通过循环不断调用方块的下移方法
- 直到下移失败(碰撞),此时方块已到达底部
- 这是高级玩家常用的技巧,可以快速放置方块并获得额外分数
19. 方块类定义
class Tetromino: \"\"\"表示当前下落的方块\"\"\" def __init__(self): self.shape_data = random.choice(SHAPES_COLORS) self.shape = self.shape_data[0] self.color = self.shape_data[1] self.row = -len(self.shape) + 1 self.col = COLS // 2 - len(self.shape[0]) // 2 def move(self, dr=1, dc=0): \"\"\"移动方块\"\"\" self.row += dr self.col += dc if check_collision(self): self.row -= dr self.col -= dc return False return True def rotate(self): \"\"\"旋转方块\"\"\" # O形方块不需要旋转 if self.color == YELLOW: return True original_shape = self.shape self.shape = rotate_matrix(self.shape) # 如果旋转后发生碰撞,则恢复原状 if check_collision(self): self.shape = original_shape return False return True
Tetromino
类封装了方块的所有属性和行为:
__init__
:随机选择一种方块形状,初始化位置(顶部居中)move()
:移动方块(dr为垂直方向,dc为水平方向),结合碰撞检测确保移动合法rotate()
:旋转方块(O形除外),如果旋转后碰撞则恢复原状(旋转无效)
20. 主游戏函数
def main(): running = True current_piece = Tetromino() next_piece = Tetromino() # 下一个方块 fall_time = 0 score = 0 fall_speed = calculate_speed(score) # 根据分数计算初始速度 fast_drop = False # 快速下落标志 try: while running: delta_time = clock.tick(FPS) fall_time += delta_time # 事件处理(省略部分代码) # 自动下落逻辑(省略部分代码) # 绘制游戏元素 screen.fill(BLACK) draw_grid(screen) draw_piece(screen, current_piece) draw_sidebar(screen, score, next_piece) pygame.display.flip() except Exception as e: print(f\"游戏发生错误: {e}\") pygame.quit() sys.exit()
main()
函数是游戏的主循环,控制整个游戏流程:
- 初始化游戏状态:当前方块、下一个方块、分数、下落速度等
- 事件处理:监听键盘和鼠标事件,处理用户输入
- 自动下落:根据时间和速度参数,控制方块自动下落
- 碰撞处理:当方块落地后,合并到网格并检查是否有满行
- 状态更新:消除满行后更新分数和下落速度
- 绘制刷新:每帧重新绘制所有游戏元素并刷新屏幕
21. 程序入口
if __name__ == \"__main__\": main()
标准的Python程序入口:当脚本直接运行时,调用main()
函数启动游戏;如果作为模块导入,则不自动运行。
通过以上模块的协同工作,实现了一个功能完整的俄罗斯方块游戏,包含方块移动、旋转、消除、计分等核心玩法,以及友好的用户界面。
运行结果
完整代码
import pygameimport randomimport sys# 初始化Pygamepygame.init()# 配置参数CELL_SIZE = 30 # 每个网格的像素大小COLS, ROWS = 10, 20 # 游戏区域的列数和行数SIDEBAR_WIDTH = 150 # 侧边栏宽度WINDOW_WIDTH, WINDOW_HEIGHT = CELL_SIZE * COLS + SIDEBAR_WIDTH, CELL_SIZE * ROWSFPS = 30# 定义颜色BLACK = (0, 0, 0)GRAY = (128, 128, 128)CYAN = (0, 255, 255)YELLOW = (255, 255, 0)MAGENTA = (255, 0, 255)GREEN = (0, 255, 0)RED = (255, 0, 0)BLUE = (0, 0, 255)ORANGE = (255, 165, 0)WHITE = (255, 255, 255)SIDEBAR_COLOR = (50, 50, 50)HIGHLIGHT_COLOR = (100, 100, 255)SHAPES_COLORS = [ ([[1, 1, 1, 1]], CYAN), # I形 ([[1, 1], [1, 1]], YELLOW), # O形 ([[0, 1, 0], [1, 1, 1]], MAGENTA), # T形 ([[1, 1, 0], [0, 1, 1]], GREEN), # S形 ([[0, 1, 1], [1, 1, 0]], RED), # Z形 ([[1, 0, 0], [1, 1, 1]], BLUE), # J形 ([[0, 0, 1], [1, 1, 1]], ORANGE) # L形]# 创建窗口screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))pygame.display.set_caption(\"俄罗斯方块 - Pygame\")clock = pygame.time.Clock()# 初始化中文字体try: # 尝试使用系统中文字体 font_title = pygame.font.SysFont(\"simhei\", 28) # 标题字体 font_large = pygame.font.SysFont(\"simhei\", 24) # 大字体 font_medium = pygame.font.SysFont(\"simhei\", 20) # 中等字体 font_small = pygame.font.SysFont(\"simhei\", 18) # 小字体except: # 如果找不到中文字体,使用默认字体 font_title = pygame.font.Font(None, 28) font_large = pygame.font.Font(None, 24) font_medium = pygame.font.Font(None, 20) font_small = pygame.font.Font(None, 18)# 游戏网格初始化grid = [[0] * COLS for _ in range(ROWS)]def draw_grid(surface): \"\"\"绘制当前网格状态\"\"\" for r in range(ROWS): for c in range(COLS): val = grid[r][c] rect = pygame.Rect(c * CELL_SIZE, r * CELL_SIZE, CELL_SIZE, CELL_SIZE) if val == 0: pygame.draw.rect(surface, BLACK, rect) pygame.draw.rect(surface, GRAY, rect, width=1) else: pygame.draw.rect(surface, val, rect)def draw_piece(surface, piece): \"\"\"绘制当前下落的方块\"\"\" shape, color = piece.shape, piece.color for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = (piece.col + c) * CELL_SIZE y = (piece.row + r) * CELL_SIZE rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) pygame.draw.rect(surface, color, rect) pygame.draw.rect(surface, GRAY, rect, width=1)def draw_sidebar(surface, score, next_piece): \"\"\"绘制侧边栏\"\"\" # 绘制侧边栏背景 sidebar_rect = pygame.Rect(CELL_SIZE * COLS, 0, SIDEBAR_WIDTH, WINDOW_HEIGHT) pygame.draw.rect(surface, SIDEBAR_COLOR, sidebar_rect) # 绘制分隔线 pygame.draw.line(surface, GRAY, (CELL_SIZE * COLS, 0), (CELL_SIZE * COLS, WINDOW_HEIGHT), 2) # 绘制标题 title_text = font_title.render(\"俄罗斯方块\", True, HIGHLIGHT_COLOR) title_rect = title_text.get_rect(center=(CELL_SIZE * COLS + SIDEBAR_WIDTH // 2, 25)) surface.blit(title_text, title_rect) # 绘制分数区域 score_y = 60 pygame.draw.line(surface, GRAY, (CELL_SIZE * COLS + 10, score_y - 10), (WINDOW_WIDTH - 10, score_y - 10), 1) score_label = font_large.render(\"当前分数\", True, WHITE) surface.blit(score_label, (CELL_SIZE * COLS + 20, score_y)) score_value = font_large.render(str(score), True, HIGHLIGHT_COLOR) surface.blit(score_value, (CELL_SIZE * COLS + 30, score_y + 30)) # 绘制下一个方块区域 next_y = 130 pygame.draw.line(surface, GRAY, (CELL_SIZE * COLS + 10, next_y - 10), (WINDOW_WIDTH - 10, next_y - 10), 1) next_label = font_large.render(\"下一个方块\", True, WHITE) surface.blit(next_label, (CELL_SIZE * COLS + 20, next_y)) # 绘制下一个方块预览 if next_piece: draw_next_piece(surface, next_piece, next_y + 40) # 绘制操作说明区域 controls_y = 250 pygame.draw.line(surface, GRAY, (CELL_SIZE * COLS + 10, controls_y - 10), (WINDOW_WIDTH - 10, controls_y - 10), 1) controls_label = font_large.render(\"操作说明\", True, WHITE) surface.blit(controls_label, (CELL_SIZE * COLS + 20, controls_y)) controls = [ \"← → : 左右移动\", \"↑ : 旋转方块\", \"↓ : 软降\", \"空格 : 硬降\" ] for i, text in enumerate(controls): ctrl_text = font_medium.render(text, True, WHITE) surface.blit(ctrl_text, (CELL_SIZE * COLS + 20, controls_y + 35 + i * 25)) # 绘制游戏提示 tip_y = controls_y + 35 + len(controls) * 25 + 20 pygame.draw.line(surface, GRAY, (CELL_SIZE * COLS + 10, tip_y - 10), (WINDOW_WIDTH - 10, tip_y - 10), 1) tip_label = font_large.render(\"游戏提示\", True, WHITE) surface.blit(tip_label, (CELL_SIZE * COLS + 20, tip_y)) tips = [ \"填满一行消除\", \"消除越多得分越高\" ] for i, text in enumerate(tips): tip_text = font_small.render(text, True, WHITE) surface.blit(tip_text, (CELL_SIZE * COLS + 20, tip_y + 35 + i * 22))def draw_next_piece(surface, piece, start_y): \"\"\"绘制下一个方块预览\"\"\" shape, color = piece.shape, piece.color # 计算预览区域中心位置 preview_x = CELL_SIZE * COLS + SIDEBAR_WIDTH // 2 - len(shape[0]) * CELL_SIZE // 2 for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = preview_x + c * CELL_SIZE y = start_y + r * CELL_SIZE rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) pygame.draw.rect(surface, color, rect) pygame.draw.rect(surface, GRAY, rect, width=1)def check_collision(piece, test_shape=None): \"\"\"检测方块是否与边界或已有方块发生碰撞\"\"\" shape = test_shape if test_shape is not None else piece.shape for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = piece.col + c y = piece.row + r if x < 0 or x >= COLS or y >= ROWS: return True if y >= 0 and grid[y][x]: return True return Falsedef merge_piece_to_grid(piece): \"\"\"将当前方块合并到游戏网格中\"\"\" shape, color = piece.shape, piece.color for r, row in enumerate(shape): for c, cell in enumerate(row): if cell: x = piece.col + c y = piece.row + r if 0 <= y < ROWS and 0 <= x < COLS: grid[y][x] = colordef rotate_matrix(matrix): \"\"\"旋转矩阵90度(顺时针)\"\"\" # 先转置矩阵,然后水平翻转 return [list(row) for row in zip(*matrix[::-1])]def clear_lines(): \"\"\"清除满行并返回清除的行数\"\"\" lines_cleared = 0 # 从底部开始检查每一行 for r in range(ROWS - 1, -1, -1): # 检查当前行是否已满 if all(grid[r][c] != 0 for c in range(COLS)): # 删除这一行 del grid[r] # 在顶部添加一个空行 grid.insert(0, [0] * COLS) lines_cleared += 1 # 因为删除了一行,所以需要重新检查当前行(原来的下一行) r += 1 return lines_cleareddef calculate_score(lines_cleared): \"\"\"根据消除行数计算分数\"\"\" if lines_cleared == 1: return 100 elif lines_cleared == 2: return 300 elif lines_cleared == 3: return 500 elif lines_cleared == 4: return 800 return 0def calculate_speed(score): \"\"\"根据分数计算下落速度\"\"\" # 基础速度为500毫秒,每1000分减少50毫秒,最低100毫秒 speed = max(500 - (score // 1000) * 50, 100) return speeddef hard_drop(piece): \"\"\"硬降:方块直接落到最底部\"\"\" while piece.move(dr=1): passclass Tetromino: \"\"\"表示当前下落的方块\"\"\" def __init__(self): self.shape_data = random.choice(SHAPES_COLORS) self.shape = self.shape_data[0] self.color = self.shape_data[1] self.row = -len(self.shape) + 1 self.col = COLS // 2 - len(self.shape[0]) // 2 def move(self, dr=1, dc=0): \"\"\"移动方块\"\"\" self.row += dr self.col += dc if check_collision(self): self.row -= dr self.col -= dc return False return True def rotate(self): \"\"\"旋转方块\"\"\" # O形方块不需要旋转 if self.color == YELLOW: return True original_shape = self.shape self.shape = rotate_matrix(self.shape) # 如果旋转后发生碰撞,则恢复原状 if check_collision(self): self.shape = original_shape return False return Truedef main(): running = True current_piece = Tetromino() next_piece = Tetromino() # 下一个方块 fall_time = 0 score = 0 fall_speed = calculate_speed(score) # 根据分数计算初始速度 fast_drop = False # 快速下落标志 try: while running: delta_time = clock.tick(FPS) fall_time += delta_time for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: current_piece.move(dc=-1) elif event.key == pygame.K_RIGHT: current_piece.move(dc=1) elif event.key == pygame.K_DOWN: # 快速下落 fast_drop = True elif event.key == pygame.K_UP: # 旋转方块 current_piece.rotate() elif event.key == pygame.K_SPACE: # 硬降 hard_drop(current_piece) elif event.type == pygame.KEYUP: if event.key == pygame.K_DOWN: # 取消快速下落 fast_drop = False # 根据是否快速下落调整下落速度 current_fall_speed = fall_speed // 10 if fast_drop else fall_speed # 自动下落 if fall_time > current_fall_speed: if not current_piece.move(dr=1): merge_piece_to_grid(current_piece) # 清除满行 lines_cleared = clear_lines() if lines_cleared > 0: # 计算并添加分数 score_increase = calculate_score(lines_cleared) score += score_increase print(f\"消除了 {lines_cleared} 行! 获得 {score_increase} 分,总分: {score}\") # 根据新分数重新计算速度 fall_speed = calculate_speed(score) # 更新当前方块和下一个方块 current_piece = next_piece next_piece = Tetromino() if check_collision(current_piece): running = False # 游戏结束 fall_time = 0 screen.fill(BLACK) draw_grid(screen) draw_piece(screen, current_piece) draw_sidebar(screen, score, next_piece) pygame.display.flip() except Exception as e: print(f\"游戏发生错误: {e}\") pygame.quit() sys.exit()if __name__ == \"__main__\": main()