Python制作俄罗斯方块游戏代码【完整代码】
定义颜色基础色:黑、白、灰用于网格线方块色:为7种不同形状的方块块分配了独特颜色(青色、黄色、洋红色等)界面色:侧边栏背景色和高亮色,用于区分不同UI区域([[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]],
1. 导入库与初始化
import pygame
import random
import sys
# 初始化Pygame
pygame.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 * ROWS
FPS = 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 pygame
import random
import sys
# 初始化Pygame
pygame.init()
# 配置参数
CELL_SIZE = 30 # 每个网格的像素大小
COLS, ROWS = 10, 20 # 游戏区域的列数和行数
SIDEBAR_WIDTH = 150 # 侧边栏宽度
WINDOW_WIDTH, WINDOW_HEIGHT = CELL_SIZE * COLS + SIDEBAR_WIDTH, CELL_SIZE * ROWS
FPS = 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 False
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
def 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_cleared
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
def calculate_speed(score):
"""根据分数计算下落速度"""
# 基础速度为500毫秒,每1000分减少50毫秒,最低100毫秒
speed = max(500 - (score // 1000) * 50, 100)
return speed
def hard_drop(piece):
"""硬降:方块直接落到最底部"""
while piece.move(dr=1):
pass
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
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
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()
更多推荐
所有评论(0)