一、需求分析
实现了一个经典的俄罗斯方块小游戏,主要满足以下需求:
1.图形界面
使用 pygame 库创建一个可视化的游戏窗口,展示游戏的各种元素,如游戏区域、方块、分数等信息。
2.游戏逻辑
实现方块的生成、移动、旋转、下落和锁定等基本操作,同时检测方块是否超出边界或与已有方块重叠。
3.计分系统
根据玩家消除的行数来计算分数和等级,等级的提升会加快方块的下落速度。
4.用户交互
支持用户通过键盘控制方块的移动、旋转和快速下落,游戏结束后可按 R 键重新开始。
5.提示信息
在游戏界面显示下一个方块预览、分数、等级以及操作说明,游戏结束时给出相应提示。
二、关键模块
1.初始化
import pygame
import random
import sys
# 初始化pygame
pygame.init()
# 颜色定义
BACKGROUND_COLOR = (214, 226, 251) # 背景色
# ... 其他颜色定义
# 游戏设置
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
SCREEN_WIDTH = GRID_WIDTH * GRID_SIZE + 200
SCREEN_HEIGHT = GRID_HEIGHT * GRID_SIZE
# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("俄罗斯方块")
# 使用系统自带的中文字体
try:
font = pygame.font.SysFont("PingFang", 20)
large_font = pygame.font.SysFont("PingFang", 40)
except:
font = pygame.font.SysFont(None, 20)
large_font = pygame.font.SysFont(None, 40)
此模块负责导入必要的库,初始化 pygame ,定义游戏所需的颜色、尺寸等常量,创建游戏窗口并设置字体。
2.Tetris 类模块
class Tetris:
def __init__(self):
self.grid = [[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.fall_speed = 0.5 # 初始下落速度(秒)
self.fall_time = 0
# ... 其他方法
Tetris 类封装了游戏的核心逻辑,包括游戏状态的初始化、方块的生成、移动、旋转、锁定以及行消除和计分等功能。
3.主游戏循环模块
def main():
clock = pygame.time.Clock()
game = Tetris()
while True:
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# ... 其他事件处理
# 自动下落
if not game.game_over:
current_time = pygame.time.get_ticks() / 1000
if current_time - game.fall_time > game.fall_speed:
game.fall_time = current_time
if game.valid_move(game.current_piece, 0, 1):
game.current_piece['y'] += 1
else:
game.lock_piece()
# 绘制游戏
game.draw()
pygame.display.update()
clock.tick(60)
该模块是游戏的主循环,负责处理用户输入事件,控制方块的自动下落,更新游戏状态并绘制游戏界面。
三、完整代码
import pygame
import random
import sys
# 初始化pygame
pygame.init()
# 颜色定义
BACKGROUND_COLOR = (214, 226, 251) # 背景色
GRID_COLOR = (255, 255, 255) # 游戏区域网格颜色
BLOCK_GRID_COLOR = (0, 0, 0) # 方块网格颜色
TEXT_COLOR = (0, 0, 0) # 普通文字颜色
GAME_OVER_COLOR = (255, 0, 0) # 游戏结束文字颜色
# 游戏设置
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
SCREEN_WIDTH = GRID_WIDTH * GRID_SIZE + 200
SCREEN_HEIGHT = GRID_HEIGHT * GRID_SIZE
# 方块形状
SHAPES = [
[[1, 1, 1, 1]], # I
[[1, 1], [1, 1]], # O
[[1, 1, 1], [0, 1, 0]], # T
[[1, 1, 1], [1, 0, 0]], # L
[[1, 1, 1], [0, 0, 1]], # J
[[0, 1, 1], [1, 1, 0]], # S
[[1, 1, 0], [0, 1, 1]] # Z
]
# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("俄罗斯方块")
# 使用系统自带的中文字体
try:
font = pygame.font.SysFont("PingFang", 20)
large_font = pygame.font.SysFont("PingFang", 40)
except:
font = pygame.font.SysFont(None, 20)
large_font = pygame.font.SysFont(None, 40)
class Tetris:
def __init__(self):
self.grid = [[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.fall_speed = 0.5 # 初始下落速度(秒)
self.fall_time = 0
def new_piece(self):
shape = random.choice(SHAPES)
return {
'shape': shape,
'x': GRID_WIDTH // 2 - len(shape[0]) // 2,
'y': 0
}
def valid_move(self, piece, x_offset=0, y_offset=0):
for y, row in enumerate(piece['shape']):
for x, cell in enumerate(row):
if cell:
new_x = piece['x'] + x + x_offset
new_y = piece['y'] + y + y_offset
if (new_x < 0 or new_x >= GRID_WIDTH or
new_y >= GRID_HEIGHT or
(new_y >= 0 and self.grid[new_y][new_x])):
return False
return True
def rotate_piece(self):
# 旋转方块
rotated = list(zip(*reversed(self.current_piece['shape'])))
old_shape = self.current_piece['shape']
self.current_piece['shape'] = rotated
if not self.valid_move(self.current_piece):
self.current_piece['shape'] = old_shape
def lock_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] = 1
# 检查是否有完整的行
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_cleared = 0
for y in range(GRID_HEIGHT):
if all(self.grid[y]):
lines_cleared += 1
# 移动上面的行下来
for y2 in range(y, 0, -1):
self.grid[y2] = self.grid[y2-1][:]
self.grid[0] = [0 for _ in range(GRID_WIDTH)]
# 更新分数
if lines_cleared > 0:
self.score += lines_cleared * 100 * self.level
self.level = self.score // 1000 + 1
self.fall_speed = max(0.1, 0.5 - (self.level - 1) * 0.05)
def draw(self):
# 绘制背景
screen.fill(BACKGROUND_COLOR)
# 绘制游戏区域网格
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
pygame.draw.rect(screen, GRID_COLOR,
(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE), 1)
# 绘制已锁定的方块
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
if self.grid[y][x]:
pygame.draw.rect(screen, (0, 100, 200),
(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE))
pygame.draw.rect(screen, BLOCK_GRID_COLOR,
(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE), 1)
# 绘制当前方块
if not self.game_over:
for y, row in enumerate(self.current_piece['shape']):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, (200, 0, 0),
((self.current_piece['x'] + x) * GRID_SIZE,
(self.current_piece['y'] + y) * GRID_SIZE,
GRID_SIZE, GRID_SIZE))
pygame.draw.rect(screen, BLOCK_GRID_COLOR,
((self.current_piece['x'] + x) * GRID_SIZE,
(self.current_piece['y'] + y) * GRID_SIZE,
GRID_SIZE, GRID_SIZE), 1)
# 绘制信息面板
info_x = GRID_WIDTH * GRID_SIZE + 10
# 绘制下一个方块预览
next_text = font.render("下一个:", True, TEXT_COLOR)
screen.blit(next_text, (info_x, 20))
for y, row in enumerate(self.next_piece['shape']):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, (200, 0, 0),
(info_x + x * GRID_SIZE, 50 + y * GRID_SIZE, GRID_SIZE, GRID_SIZE))
pygame.draw.rect(screen, BLOCK_GRID_COLOR,
(info_x + x * GRID_SIZE, 50 + y * GRID_SIZE, GRID_SIZE, GRID_SIZE), 1)
# 绘制分数和等级
score_text = font.render(f"分数: {self.score}", True, TEXT_COLOR)
level_text = font.render(f"等级: {self.level}", True, TEXT_COLOR)
screen.blit(score_text, (info_x, 150))
screen.blit(level_text, (info_x, 180))
# 绘制操作说明
controls = [
"操作说明:",
"← → : 左右移动",
"↑ : 旋转",
"↓ : 加速下落",
"空格: 直接落下"
]
for i, text in enumerate(controls):
control_text = font.render(text, True, TEXT_COLOR)
screen.blit(control_text, (info_x, 230 + i * 25))
# 游戏结束提示
if self.game_over:
game_over_text = large_font.render("游戏结束!", True, GAME_OVER_COLOR)
restart_text = font.render("按R键重新开始", True, GAME_OVER_COLOR)
screen.blit(game_over_text, (GRID_WIDTH * GRID_SIZE // 2 - 80, GRID_HEIGHT * GRID_SIZE // 2 - 50))
screen.blit(restart_text, (GRID_WIDTH * GRID_SIZE // 2 - 70, GRID_HEIGHT * GRID_SIZE // 2))
# 主游戏循环
def main():
clock = pygame.time.Clock()
game = Tetris()
while True:
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if game.game_over:
if event.key == pygame.K_r:
game = Tetris() # 重新开始游戏
else:
if event.key == pygame.K_LEFT and game.valid_move(game.current_piece, -1):
game.current_piece['x'] -= 1
elif event.key == pygame.K_RIGHT and game.valid_move(game.current_piece, 1):
game.current_piece['x'] += 1
elif event.key == pygame.K_DOWN and game.valid_move(game.current_piece, 0, 1):
game.current_piece['y'] += 1
elif event.key == pygame.K_UP:
game.rotate_piece()
elif event.key == pygame.K_SPACE:
while game.valid_move(game.current_piece, 0, 1):
game.current_piece['y'] += 1
game.lock_piece()
# 自动下落
if not game.game_over:
current_time = pygame.time.get_ticks() / 1000
if current_time - game.fall_time > game.fall_speed:
game.fall_time = current_time
if game.valid_move(game.current_piece, 0, 1):
game.current_piece['y'] += 1
else:
game.lock_piece()
# 绘制游戏
game.draw()
pygame.display.update()
clock.tick(60)
if __name__ == "__main__":
main()
四、代码运行方式
1.代码运行环境
Python 环境 :建议使用 Python 3.6 及以上版本。
依赖库 :需要安装 pygame 库,可以使用以下命令进行安装:
pip install pygame
2.游戏操作
左右移动 :按下键盘的左箭头 ← 或右箭头 → 可以控制方块左右移动。
旋转 :按下上箭头 ↑ 可以旋转方块。
加速下落 :按下下箭头 ↓ 可以使方块加速下落。
直接落下 :按下空格键 Space 可以让方块直接落到最底部。
重新开始 :游戏结束后,按下 R 键可以重新开始游戏。