俄罗斯方块 Python (Pygame)
首先,给代码:
import pygame import random # 基础配置 BLOCK_SIZE = 30 GRID_WIDTH = 10 GRID_HEIGHT = 20 SCREEN_W = BLOCK_SIZE * (GRID_WIDTH + 6) SCREEN_H = BLOCK_SIZE * GRID_HEIGHT # 方块形状定义 SHAPES = [ [[1,1,1,1]], [[1,1],[1,1]], [[0,1,0],[1,1,1]], [[1,0,0],[1,1,1]], [[0,0,1],[1,1,1]], [[1,1,0],[0,1,1]], [[0,1,1],[1,1,0]] ] COLORS = [(0,255,255),(255,255,0),(128,0,255),(0,0,255),(255,165,0),(0,255,0),(255,0,0)] class Tetris: def __init__(self): pygame.init() self.screen = pygame.display.set_mode((SCREEN_W, SCREEN_H)) pygame.display.set_caption("俄罗斯方块") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont(None, 36) self.grid = [[(0,0,0) for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] self.game_over = False self.score = 0 self.spawn_new() def spawn_new(self): self.shape_idx = random.randint(0, len(SHAPES)-1) self.shape = SHAPES[self.shape_idx] self.color = COLORS[self.shape_idx] self.x = GRID_WIDTH // 2 - len(self.shape[0])//2 self.y = 0 if self.check_collision(): self.game_over = True def check_collision(self, dx=0, dy=0, rot=None): mat = self.shape if rot is None else rot for yi, row in enumerate(mat): for xi, val in enumerate(row): if val: nx = self.x + xi + dx ny = self.y + yi + dy if nx <0 or nx >= GRID_WIDTH or ny >= GRID_HEIGHT: return True if ny >=0 and self.grid[ny][nx] != (0,0,0): return True return False def lock_block(self): for yi, row in enumerate(self.shape): for xi, val in enumerate(row): if val: self.grid[self.y+yi][self.x+xi] = self.color self.clear_lines() self.spawn_new() def clear_lines(self): lines = 0 for y in range(GRID_HEIGHT-1, -1, -1): if all(c != (0,0,0) for c in self.grid[y]): lines +=1 for yy in range(y,0,-1): self.grid[yy] = self.grid[yy-1].copy() self.grid[0] = [(0,0,0)]*GRID_WIDTH self.score += lines * 100 def rotate(self): # 矩阵旋转 r = list(zip(*self.shape[::-1])) r = [list(row) for row in r] if not self.check_collision(rot=r): self.shape = r def draw(self): self.screen.fill((20,20,20)) # 网格 for y in range(GRID_HEIGHT): for x in range(GRID_WIDTH): c = self.grid[y][x] rect = pygame.Rect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE-1, BLOCK_SIZE-1) pygame.draw.rect(self.screen, c, rect) # 当前方块 for yi, row in enumerate(self.shape): for xi, val in enumerate(row): if val: px = (self.x + xi)*BLOCK_SIZE py = (self.y + yi)*BLOCK_SIZE r = pygame.Rect(px, py, BLOCK_SIZE-1, BLOCK_SIZE-1) pygame.draw.rect(self.screen, self.color, r) # 分数 text = self.font.render(f"Score: {self.score}", True, (255,255,255)) self.screen.blit(text, (GRID_WIDTH*BLOCK_SIZE + 10, 50)) if self.game_over: over = self.font.render("Game Over", True, (255,0,0)) self.screen.blit(over, (GRID_WIDTH*BLOCK_SIZE + 10, 100)) pygame.display.update() def run(self): fall_timer = 0 fall_speed = 500 while True: dt = self.clock.tick(60) fall_timer += dt for e in pygame.event.get(): if e.type == pygame.QUIT: return if e.type == pygame.KEYDOWN and not self.game_over: if e.key == pygame.K_LEFT and not self.check_collision(dx=-1): self.x -=1 if e.key == pygame.K_RIGHT and not self.check_collision(dx=1): self.x +=1 if e.key == pygame.K_DOWN and not self.check_collision(dy=1): self.y +=1 if e.key == pygame.K_UP: self.rotate() # 自动下落 if fall_timer >= fall_speed and not self.game_over: if self.check_collision(dy=1): self.lock_block() else: self.y +=1 fall_timer = 0 self.draw() if __name__ == "__main__": game = Tetris() game.run()操作说明
- 左右方向键:左右移动方块
- 下方向键:快速下移
- 上方向键:旋转方块
- 关闭窗口退出游戏
功能包含
- 7 种标准俄罗斯方块、不同颜色
- 自动下落、行消除、计分系统
- 碰撞检测、方块落地锁定
- 填满顶部游戏结束判定
- 简单 UI 分数展示
俄罗斯方块 Python (Pygame) 代码逐段完整讲解
一、前置依赖与全局常量
import pygame import random # 基础配置 BLOCK_SIZE = 30 # 每个方块像素大小 GRID_WIDTH = 10 # 游戏区域横向格子数(标准10列) GRID_HEIGHT = 20 # 游戏区域纵向格子数(标准20行) SCREEN_W = BLOCK_SIZE * (GRID_WIDTH + 6) # 窗口总宽,右侧留出分数显示区域 SCREEN_H = BLOCK_SIZE * GRID_HEIGHT # 窗口总高作用:定义画布、格子尺寸,后续所有绘制、坐标计算都复用这几个常量,方便统一修改。
# 7种方块形状矩阵,1代表有方块,0代表空 SHAPES = [ [[1,1,1,1]], # I长条 [[1,1],[1,1]], # O正方形 [[0,1,0],[1,1,1]], # T型 [[1,0,0],[1,1,1]], # L型 [[0,0,1],[1,1,1]], # 反L [[1,1,0],[0,1,1]], # Z [[0,1,1],[1,1,0]] # 反Z ] # 对应7种方块的RGB颜色 COLORS = [(0,255,255),(255,255,0),(128,0,255),(0,0,255),(255,165,0),(0,255,0),(255,0,0)]核心设计:用二维矩阵描述方块轮廓,一一对应颜色,随机生成时直接配对。
二、游戏主类Tetris
所有游戏状态、逻辑、绘制全部封装在类内,结构清晰。
1. 构造函数__init__初始化游戏
def __init__(self): pygame.init() # 初始化pygame底层 self.screen = pygame.display.set_mode((SCREEN_W, SCREEN_H)) # 创建窗口 pygame.display.set_caption("俄罗斯方块") self.clock = pygame.time.Clock() # 控制帧率 self.font = pygame.font.SysFont(None, 36) # 文字字体 # 游戏网格:二维列表,存储每个格子颜色,初始全黑(0,0,0) self.grid = [[(0,0,0) for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] self.game_over = False # 游戏结束标记 self.score = 0 # 得分 self.spawn_new() # 生成第一个方块self.grid:游戏场地本体,落地的方块永久存在这里;spawn_new():调用生成新方块方法,开局生成第一块。
2.spawn_new()生成新方块
def spawn_new(self): self.shape_idx = random.randint(0, len(SHAPES)-1) # 随机选一种方块 self.shape = SHAPES[self.shape_idx] # 取出形状矩阵 self.color = COLORS[self.shape_idx] # 匹配颜色 self.x = GRID_WIDTH // 2 - len(self.shape[0])//2 # X坐标:水平居中生成 self.y = 0 # Y坐标:顶部生成 # 生成时直接碰撞 = 方块顶满屏幕,游戏结束 if self.check_collision(): self.game_over = True3.check_collision()碰撞检测(核心函数)
def check_collision(self, dx=0, dy=0, rot=None): mat = self.shape if rot is None else rot # rot传旋转后矩阵,用于旋转检测 for yi, row in enumerate(mat): for xi, val in enumerate(row): if val: # 当前位置有方块才检测 nx = self.x + xi + dx # 偏移后的X ny = self.y + yi + dy # 偏移后的Y # 1.撞左右墙壁 if nx <0 or nx >= GRID_WIDTH: return True # 2.撞底部边界 if ny >= GRID_HEIGHT: return True # 3.撞场上已落地的方块(ny>=0排除顶部出界) if ny >=0 and self.grid[ny][nx] != (0,0,0): return True return False # 无碰撞返回False参数说明:
dx/dy:移动偏移量(左右 / 下移时传入 ±1)rot:旋转后的方块矩阵,旋转前先检测会不会撞墙 / 方块
返回True= 发生碰撞,不能移动 / 旋转。
4.lock_block()方块落地锁定
方块无法继续下落时,把方块永久写入网格self.grid,然后消行、生成新方块:
def lock_block(self): for yi, row in enumerate(self.shape): for xi, val in enumerate(row): if val: self.grid[self.y+yi][self.x+xi] = self.color self.clear_lines() # 消除满行 self.spawn_new() # 出新方块5.clear_lines()满行消除 + 计分
def clear_lines(self): lines = 0 # 从最底部往上遍历 for y in range(GRID_HEIGHT-1, -1, -1): # 判断一整行全部不为黑色 = 填满 if all(c != (0,0,0) for c in self.grid[y]): lines +=1 # 当前行上方所有行整体下落一格 for yy in range(y,0,-1): self.grid[yy] = self.grid[yy-1].copy() self.grid[0] = [(0,0,0)]*GRID_WIDTH # 最顶行清空 self.score += lines * 100 # 每消除1行+100分6.rotate()方块旋转
def rotate(self): # 矩阵旋转算法:反转行 + 按列打包 r = list(zip(*self.shape[::-1])) r = [list(row) for row in r] # 旋转后不碰撞才更新方块 if not self.check_collision(rot=r): self.shape = r矩阵旋转逻辑举例:
plaintext
[[0,1,0], [1,1,1]] 旋转后 [[1,0], [1,1], [1,0]]7.draw()渲染画面
负责每一帧绘制背景、网格、当前方块、分数、游戏结束文字:
def draw(self): self.screen.fill((20,20,20)) # 填充深灰色背景 # 1.绘制场上已落地方块 for y in range(GRID_HEIGHT): for x in range(GRID_WIDTH): c = self.grid[y][x] rect = pygame.Rect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE-1, BLOCK_SIZE-1) pygame.draw.rect(self.screen, c, rect) # 2.绘制正在下落的活动方块 for yi, row in enumerate(self.shape): for xi, val in enumerate(row): if val: px = (self.x + xi)*BLOCK_SIZE py = (self.y + yi)*BLOCK_SIZE r = pygame.Rect(px, py, BLOCK_SIZE-1, BLOCK_SIZE-1) pygame.draw.rect(self.screen, self.color, r) # 3.绘制分数文字 text = self.font.render(f"Score: {self.score}", True, (255,255,255)) self.screen.blit(text, (GRID_WIDTH*BLOCK_SIZE + 10, 50)) # 4.游戏结束红字提示 if self.game_over: over = self.font.render("Game Over", True, (255,0,0)) self.screen.blit(over, (GRID_WIDTH*BLOCK_SIZE + 10, 100)) pygame.display.update() # 刷新画布BLOCK_SIZE-1:方块之间留 1 像素黑线分隔,视觉更清晰。
8.run()游戏主循环(游戏心脏)
所有事件、自动下落、画面刷新都在这里循环执行:
def run(self): fall_timer = 0 fall_speed = 500 # 自动下落间隔毫秒 while True: dt = self.clock.tick(60) # 锁60帧,dt=当前帧耗时ms fall_timer += dt # 事件监听:关闭窗口、按键 for e in pygame.event.get(): if e.type == pygame.QUIT: return if e.type == pygame.KEYDOWN and not self.game_over: if e.key == pygame.K_LEFT and not self.check_collision(dx=-1): self.x -=1 if e.key == pygame.K_RIGHT and not self.check_collision(dx=1): self.x +=1 if e.key == pygame.K_DOWN and not self.check_collision(dy=1): self.y +=1 if e.key == pygame.K_UP: self.rotate() # 自动下落计时逻辑 if fall_timer >= fall_speed and not self.game_over: if self.check_collision(dy=1): self.lock_block() # 下方碰撞,锁定方块 else: self.y +=1 fall_timer = 0 # 计时器清零 self.draw() # 每帧渲染画面fall_timer计时器实现定时自动下落,不用依赖帧率;- 按键按下时先检测碰撞,无碰撞才移动坐标。
三、程序入口
if __name__ == "__main__": game = Tetris() game.run()Python 标准入口,创建游戏对象并启动主循环。
整体逻辑流程总结
- 初始化窗口、空白网格、生成第一个方块;
- 主循环持续监听键盘、计时自动下落;
- 左右 / 下移先碰撞检测,合法则修改坐标;上键执行旋转;
- 计时到自动下落一格,下方碰撞则锁定方块;
- 锁定后扫描所有行,消除满行并加分;
- 顶部生成新方块,生成时直接碰撞 → Game Over;
- 每一帧绘制网格、活动方块、分数文字。