您现在的位置是:首页 >学无止境 >Pygame俄罗斯方块网站首页学无止境

Pygame俄罗斯方块

DryHeyyy 2025-02-21 12:01:02
简介Pygame俄罗斯方块

以下代码基于Pycharm编写,python版本为3.10,pygame版本为2.6.1。若有帮助,请给一个免费的赞哈~


一、效果展示


二、源码展示

import pygame as pg
import sys
import random

# 初始化 pg
pg.init()

# 颜色表
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PURPLE = (255, 0, 255)

# 设置窗口大小
SIZE = 20
GRID_W, GRID_H = 35, 35
GAME_WIDTH, GAME_HEIGHT = 20, 35
WIDTH = SIZE * GRID_W
HEIGHT = SIZE * GRID_H
screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("俄罗斯方块")
clock = pg.time.Clock()


# 随机选择一个颜色
def get_random_color():
    color = [RED, GREEN, BLUE, PURPLE]
    return random.choice(color)


# 随机选择一个形状
def get_random_shape():
    shapes = [
        [[1, 1, 1, 1]],  # I
        [[1, 1], [1, 1]],  # 0
        [[0, 1, 0], [1, 1, 1]],  # T
        [[1, 0, 0], [1, 1, 1]],  # L
        [[0, 0, 1], [1, 1, 1]],  # J
        [[0, 1, 1], [1, 1, 0]],  # S
        [[1, 1, 0], [0, 1, 1]]  # Z
    ]
    return random.choice(shapes)


# 方块类
class Tetris:
    def __init__(self):
        self.shape = get_random_shape()
        self.color = get_random_color()
        self.x = GAME_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0

    def image(self):
        return self.shape

    def rotate(self):
        self.shape = [list(row) for row in zip(*self.shape[::-1])]


# 游戏板类
class Board:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.grid = [[0 for _ in range(width)] for _ in range(height)]
        self.score = 0

    def clear_lines(self):
        lines_to_clear = [i for i in range(self.height) if all(self.grid[i])]
        for i in lines_to_clear:
            del self.grid[i]
            self.grid.insert(0, [0 for _ in range(self.width)])
            self.score += 20  # 每消除一行,得分加20

    def is_collision(self, tetromino):
        for y, row in enumerate(tetromino.shape):
            for x, cell in enumerate(row):
                if cell:
                    if (tetromino.y + y >= self.height or
                            tetromino.x + x < 0 or
                            tetromino.x + x >= self.width or
                            self.grid[tetromino.y + y][tetromino.x + x]):
                        return True
        return False

    def place_tetromino(self, tetromino):
        for y, row in enumerate(tetromino.shape):
            for x, cell in enumerate(row):
                if cell:
                    self.grid[tetromino.y + y][tetromino.x + x] = tetromino.color


# 绘制方块
def draw_tetromino(screen, tetromino, offset_x=0, offset_y=0):
    shape = tetromino.image()
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                rect = pg.Rect(
                    (tetromino.x + x + offset_x) * SIZE,
                    (tetromino.y + y + offset_y) * SIZE,
                    SIZE,
                    SIZE
                )
                pg.draw.rect(screen, tetromino.color, rect)


# 绘制游戏界面
def draw_board(board):
    # 边框绘制
    height = SIZE * GRID_H
    game_w = SIZE * GAME_WIDTH
    pg.draw.rect(screen, WHITE, (game_w, 0, SIZE, height))
    pg.draw.rect(screen, WHITE, (game_w, 200, 400, SIZE))
    for y in range(board.height):
        for x in range(board.width):
            if board.grid[y][x]:
                rect = pg.Rect(x * SIZE, y * SIZE, SIZE, SIZE)
                pg.draw.rect(screen, board.grid[y][x], rect)
                pg.draw.rect(screen, WHITE, rect, 1)


# 显示下一个方块和得分
def draw_sidebar(screen, next_tetromino, score):
    font_path = "C:WindowsFontsmsyh.ttc"
    font = pg.font.Font(font_path, 24)

    # 显示下一个方块
    text_surface = font.render("下一个方块:", True, WHITE)
    screen.blit(text_surface, (SIZE * GAME_WIDTH + 30, 20))

    # 绘制下一个方块
    for y, row in enumerate(next_tetromino.shape):
        for x, cell in enumerate(row):
            if cell:
                rect = pg.Rect(
                    SIZE * GAME_WIDTH + 140 + x * SIZE,
                    80 + y * SIZE,
                    SIZE,
                    SIZE
                )
                pg.draw.rect(screen, next_tetromino.color, rect)

    # 显示得分
    text_surface = font.render(f"目前得分: {score}", True, WHITE)
    screen.blit(text_surface, (SIZE * GAME_WIDTH + 30, 140))


# 提示文字显示
def text():
    h = 250
    text = ["左移 ←", "右移 →", "加速下降 ↓", "旋转 ↑", "暂停 space"]
    font = pg.font.Font(r"C:WindowsFontsmsyh.ttc", 36)
    for i in text:
        text_surface = font.render(i, True, WHITE)
        screen.blit(text_surface, (SIZE * GRID_W - 250, h))
        h += 80


def game_over_text(score):
    font = pg.font.Font(r"C:WindowsFontsmsyh.ttc", 36)
    screen.fill(BLACK)  # 填充背景为黑色
    text_surface = font.render("游戏结束!最终得分:{}".format(score), True, WHITE)
    text_rect = text_surface.get_rect(center=(WIDTH//2, HEIGHT//2))
    screen.blit(text_surface, text_rect)
    pg.display.flip()
    pg.time.wait(3000)  # 显示3秒


def main():
    board = Board(GAME_WIDTH, GAME_HEIGHT)
    current_t = Tetris()
    next_t = Tetris()
    fall_speed = 400
    last_fall = pg.time.get_ticks()
    move_delay = 50  # 移动延迟(ms)
    last_move = pg.time.get_ticks()
    paused = False
    game_over = False

    while not game_over:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()
                sys.exit()
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_UP:
                    current_t.rotate()
                    if board.is_collision(current_t):
                        for _ in range(3):
                            current_t.rotate()
                elif event.key == pg.K_SPACE:
                    paused = not paused

        # 下落速度提升
        if board.score == 100:
            fall_speed = 100

        if not paused:
            current_time = pg.time.get_ticks()
            keys = pg.key.get_pressed()

            # 左右移动
            if keys[pg.K_LEFT] and current_time - last_move > move_delay:
                current_t.x -= 1
                if board.is_collision(current_t):
                    current_t.x += 1
                last_move = current_time
            if keys[pg.K_RIGHT] and current_time - last_move > move_delay:
                current_t.x += 1
                if board.is_collision(current_t):
                    current_t.x -= 1
                last_move = current_time

            # 加速下落
            if keys[pg.K_DOWN]:
                current_t.y += 1
                if board.is_collision(current_t):
                    current_t.y -= 1

            # 自然下落
            if current_time - last_fall > fall_speed:
                current_t.y += 1
                if board.is_collision(current_t):
                    current_t.y -= 1
                    board.place_tetromino(current_t)
                    board.clear_lines()
                    current_t = next_t  # 切换为下一个方块
                    next_t = Tetris()
                    if board.is_collision(current_t):
                        game_over = True
                last_fall = current_time

            screen.fill(BLACK)
            text()
            draw_board(board)
            draw_tetromino(screen, current_t)
            draw_sidebar(screen, next_t, board.score)  # 绘制侧边栏
            pg.display.flip()
            clock.tick(60)

    game_over_text(board.score)
    pg.quit()
    sys.exit()


if __name__ == "__main__":
    main()

三、代码解释

1.库引用

import pygame as pg
import sys
import random

其中sys和random都属于python的内置库,无需下载。pygame属于第三方库,提供处理事件和图像的功能,若您的电脑未安装pygame,可以利用pip下载。若您用的是Pycharm,也可以在设置中的解释器中下载软件包,这样不需要将下载好的库放到pycharm创建的虚拟环境(.venv)文件夹的lib文件夹中,可以稍微省一点事(也许)

2.全局设置

# 初始化 pg
pg.init()

# 颜色表
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PURPLE = (255, 0, 255)

# 设置窗口大小
SIZE = 20
GRID_W, GRID_H = 35, 35
GAME_WIDTH, GAME_HEIGHT = 20, 35
WIDTH = SIZE * GRID_W
HEIGHT = SIZE * GRID_H
screen = pg.display.set_mode((WIDTH, HEIGHT))  # 创建窗口
pg.display.set_caption("俄罗斯方块")  # 设置窗口标题
clock = pg.time.Clock()  # 设置游戏时钟

首先是.init(),用于初始化所有 Pygame 模块。在使用 Pygame 开发游戏或图形应用程序之前,通常需要调用这个函数来确保所有必要的组件都已正确设置和准备就绪。

颜色表创建,便于之后的调用。颜色使用的是RGB(Red, Green, Blue)颜色模型,每个颜色由三个整数值组成,分别代表红色、绿色和蓝色的强度。每个颜色分量的取值范围是0到255。如上述代码所示,红色就是(255,0,0)

之后是游戏界面的参数设置。将整个界面定为网格,并且分为游戏区和提示区。我设置每个网格的大小(SIZE)为20,界面大小为35*35,游戏区大小为20*35。

3.类介绍

3.1 Tetris方块类

# 方块类
class Tetris:
    def __init__(self):
        self.shape = get_random_shape()
        self.color = get_random_color()
        self.x = GAME_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0

    def image(self):
        return self.shape

    def rotate(self):
        self.shape = [list(row) for row in zip(*self.shape[::-1])]

构造函数__init__( )中,我创建了方块的属性,其中包括形状,颜色,以及初始掉落的坐标。在调用的函数中通过random.choice()函数,随机选取形状和颜色。并且规定初始方块掉落位置为游戏区域的中部最上方。

image()方法:用于获取当前方块的形状。

def image(self):
    return self.shape

rotate()方法:顺时针转动90度。下面来详细解释一下:

def rotate(self):
    self.shape = [list(row) for row in zip(*self.shape[::-1])]

  • self.shape[::-1]:利用切片操作,先反转矩阵的行顺序。
  • zip(*self.shape[::-1]):其中 * 操作符可以将一个可迭代对象解包成独立的参数传递给函数。将反转后的矩阵进行转置,也就是行变为列。第一行变为第一列,第二行变为第二列,以此类推。但此时生成的是一个包含元组的迭代器,也就是每一行不是列表,而是元组。
  • 最后,通过列表推导式,将每一行的元组再换成列表,让后续代码可以读取旋转后的形状。

3.2  Board游戏板类

# 游戏板类
class Board:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.grid = [[0 for _ in range(width)] for _ in range(height)]
        self.score = 0

    def clear_lines(self):
        lines_to_clear = [i for i in range(self.height) if all(self.grid[i])]
        for i in lines_to_clear:
            del self.grid[i]
            self.grid.insert(0, [0 for _ in range(self.width)])
            self.score += 20  # 每消除一行,得分加20

    def is_collision(self, tetromino):
        for y, row in enumerate(tetromino.shape):
            for x, cell in enumerate(row):
                if cell:
                    if (tetromino.y + y >= self.height or
                            tetromino.x + x < 0 or
                            tetromino.x + x >= self.width or
                            self.grid[tetromino.y + y][tetromino.x + x]):
                        return True
        return False

    def place_tetromino(self, tetromino):
        for y, row in enumerate(tetromino.shape):
            for x, cell in enumerate(row):
                if cell:
                    self.grid[tetromino.y + y][tetromino.x + x] = tetromino.color

创建一个游戏板类Board来实现俄罗斯方块主要属性,例如清除满行、碰撞检测以及放置方块等。构造函数__init__( )中,包含游戏区域的长宽、游戏网格、得分情况。

clear_lines()方法:用于检测并清理游戏板上的满行,同时更新玩家的得分,清除一行得到20分。

def clear_lines(self):
    lines_to_clear = [i for i in range(self.height) if all(self.grid[i])]
    for i in lines_to_clear:
        del self.grid[i]
        self.grid.insert(0, [0 for _ in range(self.width)])
        self.score += 20  # 每消除一行,得分加20

1. 检测满行:

  • 使用列表推导式 [i for i in range(self.height) if all(self.grid[i])] 遍历每一行 i,检查该行的所有单元格是否都为 1(表示被方块占据)。
  • all(self.grid[i]) 会在所有单元格为 1 时返回 True,否则返回 False
  • 将所有满足条件的行索引 i 存储在 lines_to_clear 列表中。

2. 清除满行并下移剩余行:

  • 遍历 lines_to_clear 中的每一个行索引 i
    • del self.grid[i]:删除第 i 行。
    • self.grid.insert(0, [0 for _ in range(self.width)]):在游戏板顶部插入一行新的空行(所有单元格为 0)。
    • self.score += 20:每消除一行,玩家的得分增加 20 分。

is_collision()方法:检测当前方块(tetromino)是否与游戏板边界或其他已放置的方块发生碰撞。

def is_collision(self, tetromino):
    for y, row in enumerate(tetromino.shape):
        for x, cell in enumerate(row):
            if cell:
                if (tetromino.y + y >= self.height or
                        tetromino.x + x < 0 or
                        tetromino.x + x >= self.width or
                        self.grid[tetromino.y + y][tetromino.x + x]):
                    return True
    return False

1. 遍历方块的每个单元格:

  • 使用嵌套的 for 循环遍历 tetromino.shape 中的每一个单元格 (y, x)

2. 检查每个占据的单元格并检查碰撞条件:

  • if cell:仅检查方块实际占据的单元格(即cell == 1)。
  • tetromino.y + y >= self.height:方块的下边缘是否超出游戏板高度。
  • tetromino.x + x < 0:方块的左边缘是否超出游戏板左侧边界。
  • tetromino.x + x >= self.width:方块的右边缘是否超出游戏板右侧边界。
  • self.grid[tetromino.y + y][tetromino.x + x]:方块试图放置的位置是否已经有其他方块存在。

3. 返回结果:

  • 如果任何一个占据的单元格满足上述任一碰撞条件,则返回 True,表示发生碰撞。
  • 如果所有占据的单元格都不发生碰撞,则返回 False

place_tetromino()方法 :将当前方块永久放置在游戏板上。

def place_tetromino(self, tetromino):
    for y, row in enumerate(tetromino.shape):
        for x, cell in enumerate(row):
            if cell:
                self.grid[tetromino.y + y][tetromino.x + x] = tetromino.color

1. 遍历方块的每个单元格:

  • 使用嵌套的 for 循环遍历 tetromino.shape 中的每一个单元格 (y, x)

2. 放置方块:

  • if cell:仅处理方块实际占据的单元格(cell == 1)。
  • self.grid[tetromino.y + y][tetromino.x + x] = tetromino.color:将方块的颜色赋值给游戏板对应的位置。

4. 函数介绍

函数draw_tetromino()

用于在 Pygame 屏幕上绘制一个俄罗斯方块。

def draw_tetromino(screen, tetromino, offset_x=0, offset_y=0):
    shape = tetromino.image()
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                rect = pg.Rect(
                    (tetromino.x + x + offset_x) * SIZE,
                    (tetromino.y + y + offset_y) * SIZE,
                    SIZE,
                    SIZE
                )
                pg.draw.rect(screen, tetromino.color, rect)

1. 获取方块形状:shape = tetromino.image()

2. 遍历方块形状并绘制:

  • 双重循环遍历: 外层循环遍历每一行 (y),内层循环遍历每一列 (x)。
  • 检查单元格: if cell 判断当前单元格是否为 1,即是否为方块的一部分。
  • 计算矩形位置: 使用 tetromino.x 和 tetromino.y 确定方块在游戏网格中的位置,并加上 offset_x 和 offset_y 进行偏移,默认为0。
  • 创建矩形对象: 使用 pg.Rect 创建一个矩形对象,定义其在屏幕上的位置和大小。
  • 绘制矩形: 使用 pg.draw.rect 在屏幕上绘制矩形,颜色由 tetromino.color 指定。

函数draw_board()

表示游戏板的对象,通常包含游戏板的宽度、高度以及每个单元格的状态。

def draw_board(board):
    # 边框绘制
    height = SIZE * GRID_H
    game_w = SIZE * GAME_WIDTH
    pg.draw.rect(screen, WHITE, (game_w, 0, SIZE, height))
    pg.draw.rect(screen, WHITE, (game_w, 200, 400, SIZE))
    for y in range(board.height):
        for x in range(board.width):
            if board.grid[y][x]:
                rect = pg.Rect(x * SIZE, y * SIZE, SIZE, SIZE)
                pg.draw.rect(screen, board.grid[y][x], rect)
                pg.draw.rect(screen, WHITE, rect, 1)

1. 边框绘制:绘制右侧提示区的边框

2. 遍历游戏板的每个单元格并绘制

  • 双重 for 循环遍历游戏板的每个单元格,y 表示行,x 表示列。
  • if board.grid[y][x]:检查当前单元格是否有内容(例如,是否有方块)。
  • pg.Rect(x * SIZE, y * SIZE, SIZE, SIZE):创建一个矩形对象,表示当前单元格的位置和大小。
  • pg.draw.rect(screen, board.grid[y][x], rect):在当前单元格位置绘制一个填充矩形,颜色由 board.grid[y][x] 决定。
  • pg.draw.rect(screen, WHITE, rect, 1):在当前单元格位置绘制一个白色边框,线宽为 1,用于突出显示单元格的边界。

 函数draw_sidebar():

在界面的侧边栏中显示下一个即将出现的方块以及当前的得分。

def draw_sidebar(screen, next_tetromino, score):
    font_path = "C:WindowsFontsmsyh.ttc"
    font = pg.font.Font(font_path, 24)

    # 显示下一个方块
    text_surface = font.render("下一个方块:", True, WHITE)
    screen.blit(text_surface, (SIZE * GAME_WIDTH + 30, 20))

    # 绘制下一个方块
    for y, row in enumerate(next_tetromino.shape):
        for x, cell in enumerate(row):
            if cell:
                rect = pg.Rect(
                    SIZE * GAME_WIDTH + 140 + x * SIZE,
                    80 + y * SIZE,
                    SIZE,
                    SIZE
                )
                pg.draw.rect(screen, next_tetromino.color, rect)

    # 显示得分
    text_surface = font.render(f"目前得分: {score}", True, WHITE)
    screen.blit(text_surface, (SIZE * GAME_WIDTH + 30, 140))

1. 文字绘制

  • font_path:指定字体文件的路径,这里使用的是 Windows 系统中的微软雅黑字体(msyh.ttc)。
  • font:创建一个字体对象,字体大小为 24 像素。这个字体对象将用于渲染文本。
  • font.render:使用指定的字体渲染文本“下一个方块:”,颜色为白色(WHITE),并启用抗锯齿(True)。
  • screen.blit:将渲染好的文本表面绘制到屏幕上,位置为 (SIZE * GAME_WIDTH + 30, 20),即在游戏区域右侧 30 像素处,垂直位置 20 像素处。

2. 绘制方块图像:

  • 双重循环遍历: 外层循环遍历每一行 (y),内层循环遍历每一列 (x)。
  • 检查单元格: if cell 判断当前单元格是否为 1,即是否为方块的一部分。
  • 矩形位置: 在游戏区域右侧140像素处,垂直位置80像素处。
  • 绘制矩形: 使用 pg.draw.rect 在屏幕上绘制矩形。

3. 得分显示:

  • font.render:渲染得分文本,颜色为白色。
  • screen.blit:将得分文本绘制到屏幕上,位置为 (SIZE * GAME_WIDTH + 30, 140),即在游戏区域右侧 30 像素处,垂直位置 140 像素处。

函数text():

绘制右下方的提示文字

def text():
    h = 250
    text = ["左移 ←", "右移 →", "加速下降 ↓", "旋转 ↑", "暂停 space"]
    font = pg.font.Font(r"C:WindowsFontsmsyh.ttc", 36)
    for i in text:
        text_surface = font.render(i, True, WHITE)
        screen.blit(text_surface, (SIZE * GRID_W - 250, h))
        h += 80

1. 循环遍历:遍历 text 列表中的每一条提示信息。

2. 渲染文本:

  • font.render(i, True, WHITE):将字符串 i 渲染为图像表面,True 表示开启抗锯齿,WHITE 是文本颜色。

3. 绘制文本:

  • screen.blit(text_surface, (SIZE * GRID_W - 250, h)):将渲染好的文本表面绘制到屏幕上,位置为 (SIZE * GRID_W - 250, h)
  • SIZE  GRID_W 应该是预先定义的常量,用于确定文本的具体位置。
  • SIZE * GRID_W - 250 计算文本的横向位置,确保文本在屏幕右侧对齐。
  • h 控制文本的纵向位置,初始为 250,每绘制一条文本后增加 80 像素,确保文本之间有适当的间距。
  • 更新位置:h += 80 用于在下一次循环中将文本向下移动,以免文本重叠。

函数game_over_text():

游戏结束时显示信息,与上述text()基本同理。

def game_over_text(score):
    font = pg.font.Font(r"C:WindowsFontsmsyh.ttc", 36)  # 初始化字体
    screen.fill(BLACK)  # 填充背景为黑色
    text_surface = font.render("游戏结束!最终得分:{}".format(score), True, WHITE)
    text_rect = text_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2))
    screen.blit(text_surface, text_rect)
    pg.display.flip()
    pg.time.wait(3000)  # 显示3秒
  • 加载字体:使用微软雅黑字体,大小为36像素。
  • 填充背景:将屏幕背景设为黑色。
  • 渲染文本:创建包含“游戏结束”和最终得分的文本表面。
  • 居中显示:将文本放置在屏幕中央。
  • 更新显示:刷新屏幕以显示文本。
  • 暂停:暂停3秒,让玩家有时间查看信息

 其他函数:

# 随机选择一个颜色
def get_random_color():
    color = [RED, GREEN, BLUE, PURPLE]
    return random.choice(color)


# 随机选择一个形状
def get_random_shape():
    shapes = [
        [[1, 1, 1, 1]],  # I
        [[1, 1], [1, 1]],  # 0
        [[0, 1, 0], [1, 1, 1]],  # T
        [[1, 0, 0], [1, 1, 1]],  # L
        [[0, 0, 1], [1, 1, 1]],  # J
        [[0, 1, 1], [1, 1, 0]],  # S
        [[1, 1, 0], [0, 1, 1]]  # Z
    ]
    return random.choice(shapes)

5.主函数(主循环)

def main():
    board = Board(GAME_WIDTH, GAME_HEIGHT)
    current_t = Tetris()
    next_t = Tetris()
    fall_speed = 400
    last_fall = pg.time.get_ticks()
    move_delay = 50  # 移动延迟(ms)
    last_move = pg.time.get_ticks()
    paused = False
    game_over = False

    while not game_over:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()
                sys.exit()
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_UP:
                    current_t.rotate()
                    if board.is_collision(current_t):
                        for _ in range(3):
                            current_t.rotate()
                elif event.key == pg.K_SPACE:
                    paused = not paused

        # 下落速度提升
        if board.score == 100:
            fall_speed = 100

        if not paused:
            current_time = pg.time.get_ticks()
            keys = pg.key.get_pressed()

            # 左右移动
            if keys[pg.K_LEFT] and current_time - last_move > move_delay:
                current_t.x -= 1
                if board.is_collision(current_t):
                    current_t.x += 1
                last_move = current_time
            if keys[pg.K_RIGHT] and current_time - last_move > move_delay:
                current_t.x += 1
                if board.is_collision(current_t):
                    current_t.x -= 1
                last_move = current_time

            # 加速下落
            if keys[pg.K_DOWN]:
                current_t.y += 1
                if board.is_collision(current_t):
                    current_t.y -= 1

            # 自然下落
            if current_time - last_fall > fall_speed:
                current_t.y += 1
                if board.is_collision(current_t):
                    current_t.y -= 1
                    board.place_tetromino(current_t)
                    board.clear_lines()
                    current_t = next_t  # 切换为下一个方块
                    next_t = Tetris()
                    if board.is_collision(current_t):
                        game_over = True
                last_fall = current_time

            screen.fill(BLACK)
            text()
            draw_board(board)
            draw_tetromino(screen, current_t)
            draw_sidebar(screen, next_t, board.score)  # 绘制侧边栏
            pg.display.flip()
            clock.tick(60)

    game_over_text(board.score)
    pg.quit()
    sys.exit()


if __name__ == "__main__":
    main()
实现了俄罗斯方块游戏的核心逻辑,包括方块的生成、移动、旋转、碰撞检测、得分计算以及游戏界面的绘制。通过事件驱动和定时器控制,游戏能够响应玩家输入并保持稳定的帧率。

1. 事件处理:

    board = Board(GAME_WIDTH, GAME_HEIGHT)  # 调用Board类
    current_t = Tetris()  # 调用方块类
    next_t = Tetris()  # 调用方块类
    fall_speed = 400  # 下落速度设置
    last_fall = pg.time.get_ticks()
    move_delay = 50  # 移动延迟(ms)
    last_move = pg.time.get_ticks()
    paused = False
    game_over = False

    while not game_over:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()
                sys.exit()
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_UP:
                    current_t.rotate()
                    if board.is_collision(current_t):
                        for _ in range(3):
                            current_t.rotate()
                elif event.key == pg.K_SPACE:
                    paused = not paused
  • 退出事件:检测到 pg.QUIT 事件时,调用 pg.quit()  sys.exit() 退出游戏。
  • 按键事件:
    • 旋转方块:按下  键时,调用 current_t.rotate() 旋转当前方块。如果旋转后发生碰撞,则逆向旋转三次以恢复原状。
    • 切换暂停:按下 SPACE 键时,切换 paused 状态,暂停或恢复游戏。

2. 方块移动与处理 

        # 下落速度提升
        if board.score == 100:
            fall_speed = 100

        if not paused:
            current_time = pg.time.get_ticks()
            keys = pg.key.get_pressed()

            # 左右移动
            if keys[pg.K_LEFT] and current_time - last_move > move_delay:
                current_t.x -= 1
                if board.is_collision(current_t):
                    current_t.x += 1
                last_move = current_time
            if keys[pg.K_RIGHT] and current_time - last_move > move_delay:
                current_t.x += 1
                if board.is_collision(current_t):
                    current_t.x -= 1
                last_move = current_time

            # 加速下落
            if keys[pg.K_DOWN]:
                current_t.y += 1
                if board.is_collision(current_t):
                    current_t.y -= 1

            # 自然下落
            if current_time - last_fall > fall_speed:
                current_t.y += 1
                if board.is_collision(current_t):
                    current_t.y -= 1
                    board.place_tetromino(current_t)
                    board.clear_lines()
                    current_t = next_t  # 切换为下一个方块
                    next_t = Tetris()
                    if board.is_collision(current_t):
                        game_over = True
                last_fall = current_time
  • 左右移动:根据按键状态 (K_LEFTK_RIGHT) 和时间间隔 (move_delay) 控制方块的左右移动,并检测碰撞以防止方块移出边界或与其他方块重叠。
  • 加速下落:按下  键时,立即将方块下移一格,并检测碰撞。
  • 自然下落:根据 fall_speed 控制方块的自然下落速度,每次下落一格并检测碰撞。如果发生碰撞,固定方块位置,消除满行,并生成新的方块
  • 当新生成的方块无法放置在游戏板上时,设置 game_over = True,并进入游戏结束流程。

3. 界面绘制 

screen.fill(BLACK)
text()
draw_board(board)
draw_tetromino(screen, current_t)
draw_sidebar(screen, next_t, board.score)  # 绘制侧边栏
pg.display.flip()
clock.tick(60)
  • 填充背景:使用 screen.fill(BLACK) 将屏幕背景设为黑色。
  • 绘制元素:
    • 调用 text() 显示操作提示。
    • 调用 draw_board(board) 绘制游戏板。
    • 调用 draw_tetromino(screen, current_t) 绘制当前活动的方块。
    • 调用 draw_sidebar(screen, next_t, board.score) 绘制侧边栏,显示下一个方块和当前得分。
  • 更新显示:使用 pg.display.flip() 更新整个显示表面。
  • 控制帧率:使用 clock.tick(60) 控制游戏循环每秒运行60次,确保游戏流畅运行。

4. 游戏结束

game_over_text(board.score)
pg.quit()
sys.exit()
  • 调用 game_over_text(board.score) 显示最终得分。
  • 调用 pg.quit() 和 sys.exit() 退出游戏。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。