找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5463

积分

0

好友

735

主题
发表于 昨天 20:30 | 查看: 12| 回复: 0

在开始动手写代码之前,我们不妨先把这个经典游戏彻底拆解一下。很多人觉得俄罗斯方块很复杂,但在计算机眼里,它其实就是一个会变化的“Excel表格”而已。

游戏场景 = Excel表格:别被绚丽的动画迷惑了,游戏主界面的本质就是一个大型数字矩阵。就像你在 Excel 里看到的格子一样,有固定方块的地方格子值为 1,空着的地方就是 0。

方块下落 = 盖章:所谓的“方块下落”,本质上是程序每隔一段时间,把方块的形状(一个由 0 和 1 组成的小范围数字矩阵)“贴”在游戏地图的不同位置上。

消除一行 = 删掉Excel里全是1的行:当某一行被数字 1 完全塞满时,程序会直接删掉这一行,并在顶部补上一行全新的 0,让上面的所有列整体下移一格。

接下来,我们就使用 Python 中非常经典的游戏开发库 pygame,把这些逻辑一步步变为现实。

代码实现

为了让思路更清晰,我们将游戏的核心逻辑拆分成 4 个关键模块。

1. 方块与地图初始化

原版俄罗斯方块共有 7 种经典形状。我们用包含 0 和 1 的矩阵来定义它们,同时初始化一个 20 行 x 10 列的全 0 游戏大地图。

# 游戏网格大小
COLS, ROWS = 10, 20

# 补全 7 种传统方块形状
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1, 1], [0, 1, 0]],  # T
    [[1, 1], [1, 1]],  # O (田字)
    [[1, 1, 0], [0, 1, 1]],  # Z
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 1], [1, 0, 0]],  # L
    [[1, 1, 1], [0, 0, 1]]   # J
]

# 初始化大地图:20行10列的二维列表,初始全为0
game_field = [[0 for _ in range(COLS)] for _ in range(ROWS)]

2. 方块旋转与碰撞检测

方块旋转的本质是矩阵翻转——将行变成列,并反转顺序。

方块在移动或旋转时绝对不能穿墙,也不能穿过已经固定的旧方块。因此,每次动作前都必须进行“预判”。这里我们用 check_collision 函数来把关。

# 碰撞检测函数:判断方块在指定位置是否合法
def check_collision(shape, offset_x, offset_y):
    for r_idx, row in enumerate(shape):
        for c_idx, val in enumerate(row):
            if val:
                new_x = offset_x + c_idx
                new_y = offset_y + r_idx
                # 检查是否越界(左右边界、底部)或撞到已有方块
                if new_x < 0 or new_x >= COLS or new_y >= ROWS:
                    return True
                if new_y >= 0 and game_field[new_y][new_x]:
                    return True
    return False

# 顺时针旋转矩阵
def rotate_shape(shape):
    return [list(x) for x in zip(*shape[::-1])]

3. 消行并得分

当某一行不存在 0 时,说明它被填满了。我们将其剔除,并在大地图顶部塞入一行全新的 0。每消除一行加 100 分。

def clear_lines():
    global game_field, score
    # 过滤掉全是1的行,只保留没满的行
    new_field = [row for row in game_field if any(val == 0 for val in row)]
    cleared = ROWS - len(new_field)  # 删掉了几行

    # 补齐上方空行
    for _ in range(cleared):
        new_field.insert(0, [0 for _ in range(COLS)])

    game_field = new_field
    score += cleared * 100  # 每消一行加100分

4. 游戏结束与重玩

当新生成的方块在出生点就发生碰撞,说明方块已经堆到了天花板,游戏结束。此时按下回车键(K_RETURN)即可清空分数和地图,重新开始。

# 每次生成新方块时检测
if check_collision(current_shape, block_x, block_y):
    game_over = True  # 触发游戏结束开关

# 重置游戏函数
def reset_game():
    global game_field, score, game_over, current_shape, block_x, block_y
    game_field = [[0 for _ in range(COLS)] for _ in range(ROWS)]
    score = 0
    game_over = False
    current_shape = random.choice(SHAPES)
    block_x, block_y = 3, 0

完整游戏代码

把上面的逻辑组装起来,加上画面绘制与键盘事件响应,一个完整的俄罗斯方块就诞生了。

import pygame
import random

# 初始化 pygame
pygame.init()
GRID_SIZE = 30
COLS, ROWS = 10, 20
SCREEN_WIDTH, SCREEN_HEIGHT = COLS * GRID_SIZE, ROWS * GRID_SIZE + 50 # 底部留白显示分数
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Crossin的俄罗斯方块")

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (50, 50, 50)
RED = (255, 87, 34)
BLUE = (33, 150, 243)

# 7 种经典方块
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1, 1], [0, 1, 0]],  # T
    [[1, 1], [1, 1]],  # O
    [[1, 1, 0], [0, 1, 1]],  # Z
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 1], [1, 0, 0]],  # L
    [[1, 1, 1], [0, 0, 1]]   # J
]

game_field = [[0 for _ in range(COLS)] for _ in range(ROWS)]
score = 0
game_over = False

current_shape = random.choice(SHAPES)
block_x, block_y = 3, 0

clock = pygame.time.Clock()
fall_time = 0
fall_speed = 500  # 方块每 500 毫秒下落一格

def check_collision(shape, offset_x, offset_y):
    for r_idx, row in enumerate(shape):
        for c_idx, val in enumerate(row):
            if val:
                new_x = offset_x + c_idx
                new_y = offset_y + r_idx
                if new_x < 0 or new_x >= COLS or new_y >= ROWS:
                    return True
                if new_y >= 0 and game_field[new_y][new_x]:
                    return True
    return False

def rotate_shape(shape):
    return [list(x) for x in zip(*shape[::-1])]

def clear_lines():
    global game_field, score
    new_field = [row for row in game_field if any(val == 0 for val in row)]
    cleared = ROWS - len(new_field)
    for _ in range(cleared):
        new_field.insert(0, [0 for _ in range(COLS)])
    game_field = new_field
    score += cleared * 100

def reset_game():
    global game_field, score, game_over, current_shape, block_x, block_y
    game_field = [[0 for _ in range(COLS)] for _ in range(ROWS)]
    score = 0
    game_over = False
    current_shape = random.choice(SHAPES)
    block_x, block_y = 3, 0

running = True
while running:
    screen.fill(BLACK)
    delta_time = clock.tick(60)  # 游戏主循环每秒运行60次
    fall_time += delta_time

    # 1. 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if game_over:
                if event.key == pygame.K_RETURN:  # 游戏结束时按回车重玩
                    reset_game()
            else:
                if event.key == pygame.K_LEFT:
                    if not check_collision(current_shape, block_x - 1, block_y):
                        block_x -= 1
                if event.key == pygame.K_RIGHT:
                    if not check_collision(current_shape, block_x + 1, block_y):
                        block_x += 1
                if event.key == pygame.K_DOWN:
                    if not check_collision(current_shape, block_x, block_y + 1):
                        block_y += 1
                if event.key == pygame.K_UP:  # 上方向键旋转
                    rotated = rotate_shape(current_shape)
                    if not check_collision(rotated, block_x, block_y):
                        current_shape = rotated

    # 2. 自动下落逻辑
    if not game_over:
        if fall_time >= fall_speed:
            fall_time = 0
            if not check_collision(current_shape, block_x, block_y + 1):
                block_y += 1
            else:
                # 触底锁定方块
                for r_idx, row in enumerate(current_shape):
                    for c_idx, val in enumerate(row):
                        if val and block_y + r_idx >= 0:
                            game_field[block_y + r_idx][block_x + c_idx] = 1
                clear_lines()
                # 重新生成新方块
                current_shape = random.choice(SHAPES)
                block_x, block_y = 3, 0
                if check_collision(current_shape, block_x, block_y):
                    game_over = True

    # 3. 画面渲染
    # 绘制固定的地图方块
    for r in range(ROWS):
        for c in range(COLS):
            if game_field[r][c]:
                pygame.draw.rect(screen, BLUE, (c * GRID_SIZE, r * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
            else:
                pygame.draw.rect(screen, GRAY, (c * GRID_SIZE, r * GRID_SIZE, GRID_SIZE, GRID_SIZE), 1)

    # 绘制当前下落的方块
    if not game_over:
        for r_idx, row in enumerate(current_shape):
            for c_idx, val in enumerate(row):
                if val:
                    x = (block_x + c_idx) * GRID_SIZE
                    y = (block_y + r_idx) * GRID_SIZE
                    pygame.draw.rect(screen, RED, (x, y, GRID_SIZE - 1, GRID_SIZE - 1))

    # 4. UI 文本显示
    font = pygame.font.SysFont("SimHei", 24)   # mac改为 "songti"
    score_text = font.render(f"得分: {score}", True, WHITE)
    screen.blit(score_text, (10, SCREEN_HEIGHT - 40))

    if game_over:
        over_text = font.render("游戏结束! 按回车重新开始", True, RED)
        screen.blit(over_text, (SCREEN_WIDTH // 2 - over_text.get_width() // 2, SCREEN_HEIGHT // 2 - 20))

    pygame.display.flip()

pygame.quit()

新手避坑指南

开发过程中有几个新手常遇到的坑,提前了解一下能省去不少调试时间:

1. 方块一晃眼就掉到底,根本反应不过来
这通常是因为忘记写帧率控制。如果没有 clock.tick(60) 这个限速器,Python 会以极快的速度运行循环。你可以根据实际运行情况调整这个参数。

2. 旋转时方块会卡进墙里或者报错
原因在于旋转之前没有做碰撞预判。务必先调用 check_collision 判断返回 False 后,才能执行旋转操作。

3. 中文显示不正确或代码报错 SyntaxError
中文问题分两类。一是游戏界面中的中文显示需要对应的字体支持,Windows 和 Mac 系统自带的字体名称不同,要区分设置。二是代码里的逗号、括号、引号必须是英文半角字符。很多新手在打完中文后忘了切回英文输入法,导致符号输错,这一点尤其需要注意。

看到这里,相信你已经掌握了开发《俄罗斯方块》的核心思路。但看懂不等于学会,不如现在就打开电脑,把这段代码复制进去跑一下,然后在此基础上做一些个性化优化。既能体验游戏的乐趣,又能实实在在提升编程水平,一举多得。

在云栈社区,我们一直鼓励通过实际项目来学习 Python 的各种特性,比如本次使用的 列表推导式 就极大地简化了地图初始化代码。如果你在开发中遇到任何问题,也欢迎来社区交流分享。




上一篇:OP Stack 预留区块空间框架:双层排序、溢出管理与可预测包含实现
下一篇:Python已安装库却报错ModuleNotFoundError?多环境冲突解决指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-11 08:05 , Processed in 0.809671 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表