Python 俄罗斯方块游戏
2026-01-25 19:33:20
发布于:浙江
以下内容为AI生成,作者转载
Python版
import warnings
warnings.filterwarnings("ignore",
message="pkg_resources is deprecated as an API")
import pygame
import random
import sys
import os
# 初始化pygame
pygame.init()
# 游戏常量
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 700
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
SIDEBAR_WIDTH = 200
# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 50, 50)
GREEN = (50, 255, 50)
BLUE = (50, 100, 255)
CYAN = (0, 255, 255)
MAGENTA = (255, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 165, 0)
GRAY = (128, 128, 128)
DARK_GRAY = (50, 50, 50)
BACKGROUND_COLOR = (20, 20, 40)
GRID_COLOR = (40, 40, 60)
TEXT_COLOR = (220, 220, 255)
# 方块形状定义 (7种经典形状)
SHAPES = [
# I
[[1, 1, 1, 1]],
# O
[[1, 1],
[1, 1]],
# T
[[0, 1, 0],
[1, 1, 1]],
# S
[[0, 1, 1],
[1, 1, 0]],
# Z
[[1, 1, 0],
[0, 1, 1]],
# J
[[1, 0, 0],
[1, 1, 1]],
# L
[[0, 0, 1],
[1, 1, 1]]
]
# 方块颜色 (与形状对应)
SHAPE_COLORS = [CYAN, YELLOW, MAGENTA, GREEN, RED, BLUE, ORANGE]
class Tetromino:
def __init__(self, x, y, shape_idx):
self.x = x
self.y = y
self.shape_idx = shape_idx
self.shape = SHAPES[shape_idx]
self.color = SHAPE_COLORS[shape_idx]
self.rotation = 0
def rotate(self):
# 转置矩阵并反转每一行以实现90度旋转
rows = len(self.shape)
cols = len(self.shape[0])
rotated = [[0 for _ in range(rows)] for _ in range(cols)]
for r in range(rows):
for c in range(cols):
rotated[c][rows-1-r] = self.shape[r][c]
return rotated
def get_rotated_shape(self):
shape = self.shape
for _ in range(self.rotation % 4):
rows = len(shape)
cols = len(shape[0])
rotated = [[0 for _ in range(rows)] for _ in range(cols)]
for r in range(rows):
for c in range(cols):
rotated[c][rows-1-r] = shape[r][c]
shape = rotated
return shape
def get_positions(self):
positions = []
shape = self.get_rotated_shape()
for y, row in enumerate(shape):
for x, cell in enumerate(row):
if cell:
positions.append((self.x + x, self.y + y))
return positions
class FontManager:
"""字体管理器,处理中英文字体兼容"""
def __init__(self):
self.use_chinese = True # 默认尝试使用中文
self.fonts = {}
self.load_fonts()
def load_fonts(self):
"""加载字体,先尝试中文字体,如果失败则使用英文字体"""
# 中文文本
self.texts = {
"title": "俄罗斯方块",
"next": "下一个:",
"score": "分数: {}",
"level": "等级: {}",
"lines": "消除行: {}",
"controls": "操作说明:",
"left_right": "← → : 左右移动",
"up": "↑ : 旋转",
"down": "↓ : 加速下落",
"space": "空格 : 直接落下",
"pause": "P : 暂停游戏",
"restart": "R : 重新开始",
"game_over": "游戏结束!",
"final_score": "最终分数: {}",
"press_r": "按 R 键重新开始"
}
# 英文字体备选列表
english_fonts = ['arial', 'freesansbold', 'dejavusans', 'verdana', 'tahoma']
try:
# 尝试加载中文字体
chinese_fonts = [
'msyh.ttc', # 微软雅黑
'simhei.ttf', # 黑体
'simsun.ttc', # 宋体
'DroidSansFallback.ttf', # Android字体
'wqy-microhei.ttc', # 文泉驿微米黑
]
# 查找系统字体目录
font_dirs = []
if sys.platform == 'win32':
font_dirs.append(os.path.join(os.environ['WINDIR'], 'Fonts'))
elif sys.platform == 'darwin':
font_dirs.append('/System/Library/Fonts')
font_dirs.append('/Library/Fonts')
else: # Linux
font_dirs.append('/usr/share/fonts')
font_dirs.append(os.path.expanduser('~/.fonts'))
font_found = False
font_path = None
# 尝试查找中文字体文件
for font_dir in font_dirs:
if os.path.exists(font_dir):
for font_file in chinese_fonts:
test_path = os.path.join(font_dir, font_file)
if os.path.exists(test_path):
font_path = test_path
font_found = True
break
if font_found:
break
if font_found:
# 加载中文字体 - 减小字体大小避免遮挡
self.fonts['big'] = pygame.font.Font(font_path, 36) # 减小大小
self.fonts['medium'] = pygame.font.Font(font_path, 36) # 减小大小
self.fonts['normal'] = pygame.font.Font(font_path, 28) # 减小大小
self.fonts['small'] = pygame.font.Font(font_path, 20) # 减小大小
print(f"使用中文字体: {font_path}")
else:
raise Exception("未找到中文字体")
except Exception as e:
print(f"无法加载中文字体: {e}")
print("将使用英文字体")
self.use_chinese = False
# 切换到英文文本
self.texts = {
"title": "TETRIS",
"next": "Next:",
"score": "Score: {}",
"level": "Level: {}",
"lines": "Lines: {}",
"controls": "Controls:",
"left_right": "Left/Right: Move",
"up": "Up: Rotate",
"down": "Down: Speed up",
"space": "Space: Drop",
"pause": "P: Pause",
"restart": "R: Restart",
"game_over": "GAME OVER!",
"final_score": "Final Score: {}",
"press_r": "Press R to Restart"
}
# 尝试加载英文字体 - 也减小字体大小
font_loaded = False
for font_name in english_fonts:
try:
self.fonts['big'] = pygame.font.SysFont(font_name, 48) # 减小大小
self.fonts['medium'] = pygame.font.SysFont(font_name, 36) # 减小大小
self.fonts['normal'] = pygame.font.SysFont(font_name, 28) # 减小大小
self.fonts['small'] = pygame.font.SysFont(font_name, 20) # 减小大小
font_loaded = True
print(f"使用英文字体: {font_name}")
break
except:
continue
# 如果都没有找到,使用默认字体
if not font_loaded:
self.fonts['big'] = pygame.font.Font(None, 48) # 减小大小
self.fonts['medium'] = pygame.font.Font(None, 36) # 减小大小
self.fonts['normal'] = pygame.font.Font(None, 28) # 减小大小
self.fonts['small'] = pygame.font.Font(None, 20) # 减小大小
print("使用默认字体")
def get_text(self, key, *args):
"""获取文本,支持格式化参数"""
text = self.texts.get(key, key)
if args:
return text.format(*args)
return text
def render(self, size, text, color):
"""渲染文本"""
font_key = {
48: 'big',
36: 'medium',
28: 'normal',
20: 'small'
}.get(size, 'normal')
font = self.fonts.get(font_key, self.fonts['normal'])
return font.render(text, True, color)
class TetrisGame:
def __init__(self):
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 设置窗口标题为中文
pygame.display.set_caption("俄罗斯方块")
self.clock = pygame.time.Clock()
self.font_manager = FontManager()
self.reset_game()
# 游戏区域左上角坐标
self.grid_left = (SCREEN_WIDTH - SIDEBAR_WIDTH - GRID_WIDTH * GRID_SIZE) // 2
self.grid_top = (SCREEN_HEIGHT - GRID_HEIGHT * GRID_SIZE) // 2
# 游戏速度设置
self.fall_speed = 0.5 # 方块下落速度 (秒/格)
self.fall_time = 0
# 按键重复设置
self.move_delay = 0.1
self.move_time = 0
self.last_move = None
def reset_game(self):
self.board = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
self.current_piece = self.new_piece()
self.next_piece = self.new_piece()
self.game_over = False
self.score = 0
self.level = 1
self.lines_cleared = 0
self.fall_time = 0
self.move_time = 0
def new_piece(self):
# 创建新方块
shape_idx = random.randint(0, len(SHAPES) - 1)
# 初始位置在顶部中间
start_x = GRID_WIDTH // 2 - len(SHAPES[shape_idx][0]) // 2
return Tetromino(start_x, 0, shape_idx)
def valid_move(self, piece, x_offset=0, y_offset=0, rotation=0):
# 检查移动或旋转是否有效
test_piece = Tetromino(piece.x + x_offset, piece.y + y_offset, piece.shape_idx)
test_piece.rotation = (piece.rotation + rotation) % 4
test_piece.shape = piece.shape
for x, y in test_piece.get_positions():
# 检查是否超出边界
if x < 0 or x >= GRID_WIDTH or y >= GRID_HEIGHT:
return False
# 检查是否与已有方块重叠
if y >= 0 and self.board[y][x]:
return False
return True
def lock_piece(self, piece):
# 将当前方块锁定到游戏板上
for x, y in piece.get_positions():
if y >= 0: # 确保方块在游戏区域内
self.board[y][x] = piece.color
# 检查是否有行可以消除
self.clear_lines()
# 生成新方块
self.current_piece = self.next_piece
self.next_piece = self.new_piece()
# 检查游戏是否结束
if not self.valid_move(self.current_piece):
self.game_over = True
def clear_lines(self):
# 检查并消除完整的行
lines_to_clear = []
for y in range(GRID_HEIGHT):
if all(self.board[y]):
lines_to_clear.append(y)
for line in lines_to_clear:
# 移除该行
del self.board[line]
# 在顶部添加新行
self.board.insert(0, [0 for _ in range(GRID_WIDTH)])
# 更新分数
if lines_to_clear:
self.lines_cleared += len(lines_to_clear)
self.score += (1, 2, 5, 10)[min(len(lines_to_clear)-1, 3)] * 100
self.level = self.lines_cleared // 10 + 1
self.fall_speed = max(0.05, 0.5 - (self.level - 1) * 0.05) # 随等级提高速度
def draw_board(self):
# 绘制游戏区域背景
board_rect = pygame.Rect(
self.grid_left,
self.grid_top,
GRID_WIDTH * GRID_SIZE,
GRID_HEIGHT * GRID_SIZE
)
pygame.draw.rect(self.screen, GRID_COLOR, board_rect)
pygame.draw.rect(self.screen, DARK_GRAY, board_rect, 3)
# 绘制网格线
for x in range(GRID_WIDTH + 1):
pygame.draw.line(
self.screen,
DARK_GRAY,
(self.grid_left + x * GRID_SIZE, self.grid_top),
(self.grid_left + x * GRID_SIZE, self.grid_top + GRID_HEIGHT * GRID_SIZE),
1
)
for y in range(GRID_HEIGHT + 1):
pygame.draw.line(
self.screen,
DARK_GRAY,
(self.grid_left, self.grid_top + y * GRID_SIZE),
(self.grid_left + GRID_WIDTH * GRID_SIZE, self.grid_top + y * GRID_SIZE),
1
)
# 绘制已锁定的方块
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
if self.board[y][x]:
color = self.board[y][x]
rect = pygame.Rect(
self.grid_left + x * GRID_SIZE,
self.grid_top + y * GRID_SIZE,
GRID_SIZE,
GRID_SIZE
)
pygame.draw.rect(self.screen, color, rect)
pygame.draw.rect(self.screen, BLACK, rect, 1)
# 绘制当前下落的方块
if not self.game_over:
for x, y in self.current_piece.get_positions():
if y >= 0: # 只绘制在游戏区域内的部分
rect = pygame.Rect(
self.grid_left + x * GRID_SIZE,
self.grid_top + y * GRID_SIZE,
GRID_SIZE,
GRID_SIZE
)
pygame.draw.rect(self.screen, self.current_piece.color, rect)
pygame.draw.rect(self.screen, BLACK, rect, 1)
# 绘制方块内部高光
highlight = pygame.Rect(
self.grid_left + x * GRID_SIZE + 2,
self.grid_top + y * GRID_SIZE + 2,
GRID_SIZE - 4,
GRID_SIZE - 4
)
pygame.draw.rect(self.screen, WHITE, highlight, 1)
def draw_sidebar(self):
sidebar_left = self.grid_left + GRID_WIDTH * GRID_SIZE + 20
# 绘制下一个方块预览
next_text = self.font_manager.render(28, self.font_manager.get_text("next"), TEXT_COLOR)
self.screen.blit(next_text, (sidebar_left, self.grid_top))
# 绘制下一个方块
next_block_left = sidebar_left + 30
next_block_top = self.grid_top + 50
# 绘制预览区域背景
preview_width = 5 * GRID_SIZE
preview_height = 5 * GRID_SIZE
preview_rect = pygame.Rect(
next_block_left - 10,
next_block_top - 10,
preview_width,
preview_height
)
pygame.draw.rect(self.screen, DARK_GRAY, preview_rect)
pygame.draw.rect(self.screen, GRAY, preview_rect, 2)
# 计算下一个方块在预览区域中的位置
shape = self.next_piece.shape
shape_width = len(shape[0]) * GRID_SIZE
shape_height = len(shape) * GRID_SIZE
shape_left = next_block_left + (preview_width - shape_width) // 2
shape_top = next_block_top + (preview_height - shape_height) // 2
# 绘制下一个方块
for y, row in enumerate(shape):
for x, cell in enumerate(row):
if cell:
rect = pygame.Rect(
shape_left + x * GRID_SIZE,
shape_top + y * GRID_SIZE,
GRID_SIZE,
GRID_SIZE
)
pygame.draw.rect(self.screen, self.next_piece.color, rect)
pygame.draw.rect(self.screen, BLACK, rect, 1)
# 绘制分数和等级信息
score_y = next_block_top + preview_height + 40
score_text = self.font_manager.render(28, self.font_manager.get_text("score", self.score), TEXT_COLOR)
self.screen.blit(score_text, (sidebar_left, score_y))
level_text = self.font_manager.render(28, self.font_manager.get_text("level", self.level), TEXT_COLOR)
self.screen.blit(level_text, (sidebar_left, score_y + 40))
lines_text = self.font_manager.render(28, self.font_manager.get_text("lines", self.lines_cleared), TEXT_COLOR)
self.screen.blit(lines_text, (sidebar_left, score_y + 80))
# 绘制操作说明
controls_y = score_y + 150
controls_title = self.font_manager.render(28, self.font_manager.get_text("controls"), TEXT_COLOR)
self.screen.blit(controls_title, (sidebar_left, controls_y))
controls = [
self.font_manager.get_text("left_right"),
self.font_manager.get_text("up"),
self.font_manager.get_text("down"),
self.font_manager.get_text("space"),
self.font_manager.get_text("pause"),
self.font_manager.get_text("restart")
]
for i, text in enumerate(controls):
control_text = self.font_manager.render(20, text, TEXT_COLOR)
self.screen.blit(control_text, (sidebar_left, controls_y + 35 + i * 25))
def draw_game_over(self):
# 绘制半透明覆盖层
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
self.screen.blit(overlay, (0, 0))
# 绘制游戏结束文本
game_over_text = self.font_manager.render(48, self.font_manager.get_text("game_over"), RED)
text_rect = game_over_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 50))
self.screen.blit(game_over_text, text_rect)
# 绘制最终分数
score_text = self.font_manager.render(28, self.font_manager.get_text("final_score", self.score), WHITE)
score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 20))
self.screen.blit(score_text, score_rect)
# 绘制重新开始提示
restart_text = self.font_manager.render(28, self.font_manager.get_text("press_r"), YELLOW)
restart_rect = restart_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 80))
self.screen.blit(restart_text, restart_rect)
def draw(self):
# 填充背景色
self.screen.fill(BACKGROUND_COLOR)
# 绘制游戏标题
title_text = self.font_manager.render(48, self.font_manager.get_text("title"), TEXT_COLOR)
title_rect = title_text.get_rect(center=(SCREEN_WIDTH // 2, 30))
self.screen.blit(title_text, title_rect)
# 绘制游戏区域和侧边栏
self.draw_board()
self.draw_sidebar()
# 如果游戏结束,绘制游戏结束画面
if self.game_over:
self.draw_game_over()
# 更新显示
pygame.display.flip()
def run(self):
# 游戏主循环
paused = False
last_time = pygame.time.get_ticks() / 1000.0
while True:
current_time = pygame.time.get_ticks() / 1000.0
delta_time = current_time - last_time
last_time = current_time
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
self.reset_game()
if event.key == pygame.K_p:
paused = not paused
if not self.game_over and not paused:
if event.key == pygame.K_LEFT:
if self.valid_move(self.current_piece, x_offset=-1):
self.current_piece.x -= 1
elif event.key == pygame.K_RIGHT:
if self.valid_move(self.current_piece, x_offset=1):
self.current_piece.x += 1
elif event.key == pygame.K_DOWN:
if self.valid_move(self.current_piece, y_offset=1):
self.current_piece.y += 1
elif event.key == pygame.K_UP:
if self.valid_move(self.current_piece, rotation=1):
self.current_piece.rotation = (self.current_piece.rotation + 1) % 4
elif event.key == pygame.K_SPACE:
# 硬降落 - 直接落到底部
while self.valid_move(self.current_piece, y_offset=1):
self.current_piece.y += 1
self.lock_piece(self.current_piece)
# 处理持续按键
if not self.game_over and not paused:
keys = pygame.key.get_pressed()
current_move = None
if keys[pygame.K_LEFT]:
current_move = "left"
elif keys[pygame.K_RIGHT]:
current_move = "right"
elif keys[pygame.K_DOWN]:
current_move = "down"
if current_move:
if current_move != self.last_move:
self.move_time = 0
self.last_move = current_move
self.move_time += delta_time
if self.move_time >= self.move_delay:
if current_move == "left" and self.valid_move(self.current_piece, x_offset=-1):
self.current_piece.x -= 1
elif current_move == "right" and self.valid_move(self.current_piece, x_offset=1):
self.current_piece.x += 1
elif current_move == "down" and self.valid_move(self.current_piece, y_offset=1):
self.current_piece.y += 1
self.move_time = 0
else:
self.last_move = None
# 游戏逻辑更新
if not self.game_over and not paused:
# 方块自动下落
self.fall_time += delta_time
if self.fall_time >= self.fall_speed:
if self.valid_move(self.current_piece, y_offset=1):
self.current_piece.y += 1
else:
self.lock_piece(self.current_piece)
self.fall_time = 0
# 绘制游戏
self.draw()
# 控制帧率
self.clock.tick(60)
# 运行游戏
if __name__ == "__main__":
print("启动俄罗斯方块游戏...")
print("正在检测字体...")
game = TetrisGame()
game.run()
运行这个程序,你需要安装Python
并安装PyGame:
pip install pygame
(安装指南)
或者,你可以用这个 HTML版:
HTML 版
全部评论 3
1
20小时前 来自 浙江
11
4天前 来自 广东
1啥意思?
4天前 来自 浙江
1就是蹭你的帖子,拿罐头
3天前 来自 广东
1……
又一个令我无语的……3天前 来自 浙江
1
有C++的吗?
21小时前 来自 浙江
0没有,C++ GUI工具我不知道
18小时前 来自 浙江
0




























有帮助,赞一个