> 技术文档 > Python制作俄罗斯方块游戏代码【完整代码】

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_WIDTHWINDOW_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()函数启动游戏;如果作为模块导入,则不自动运行。

通过以上模块的协同工作,实现了一个功能完整的俄罗斯方块游戏,包含方块移动、旋转、消除、计分等核心玩法,以及友好的用户界面。

运行结果

Python制作俄罗斯方块游戏代码【完整代码】

完整代码

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()