基于Pygame的俄罗斯方块游戏开发指南

俄罗斯方块作为一款经典的益智游戏,自问世以来便广受欢迎。本文将详细介绍如何使用Python的Pygame库开发一个功能完整的俄罗斯方块游戏。通过这个项目,你将学习到游戏开发的基本原理和Pygame的使用方法。

游戏设计概述

在开始编码之前,让我们先了解一下游戏的基本设计:

游戏区域

由20×30的网格组成,每个方块占据一个单元格

方块类型

7种不同形状的方块(I、O、T、J、L、S、Z),每种有独特的颜色

游戏机制

方块从顶部中央生成并自动下落,玩家可以控制移动和旋转

计分系统

消除完整行获得分数,随着分数增加游戏难度提高

游戏实现

下面是完整的游戏实现代码,我将逐步解释各个部分的功能:

提示: 在开始编码前,请确保已安装Pygame库。可以通过pip install pygame命令安装。

初始化和设置


import pygame
import random
from pygame import *

# 初始化
pygame.init()

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
ORANGE = (255, 165, 0)
YELLOW = (255, 255, 0)
GREEN = (0, 255, 0)
PURPLE = (128, 0, 128)
RED = (255, 0, 0)

# 游戏设置
CELL_SIZE = 30
GRID_WIDTH = 20
GRID_HEIGHT = 30
SCREEN_WIDTH = CELL_SIZE * (GRID_WIDTH + 6)
SCREEN_HEIGHT = CELL_SIZE * GRID_HEIGHT
GAME_AREA_LEFT = CELL_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]],  # J
    [[1, 1, 1], [0, 0, 1]],  # L
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 0], [0, 1, 1]]   # Z
]

# 方块颜色
COLORS = [CYAN, YELLOW, PURPLE, BLUE, ORANGE, GREEN, RED]
        

这段代码主要完成了游戏的初始化工作,包括导入必要的库、定义颜色常量、设置游戏参数以及方块的形状和颜色。

游戏主类

接下来是游戏的核心类Tetris,它包含了游戏的所有逻辑和功能:


class Tetris:
    def __init__(self):
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption('俄罗斯方块')
        self.clock = pygame.time.Clock()
        self.grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
        self.current_piece = self.new_piece()
        self.game_over = False
        self.score = 0
        self.font = pygame.font.SysFont('simhei', 25)

    def new_piece(self):
        # 随机选择方块和颜色
        shape = random.choice(SHAPES)
        color = COLORS[SHAPES.index(shape)]
        # 初始位置在顶部中间
        x = GRID_WIDTH // 2 - len(shape[0]) // 2
        y = 0
        return {'shape': shape, 'x': x, 'y': y, 'color': color}

    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
        

__init__方法初始化游戏窗口、网格、当前方块等基本属性。new_piece方法随机生成一个新的方块,并将其放置在网格顶部中央。valid_move方法用于检查方块在移动或旋转后是否合法,确保不会超出边界或与已锁定的方块重叠。

方块操作方法


    def rotate_piece(self):
        # 转置矩阵然后反转每一行实现旋转
        rotated = list(zip(*self.current_piece['shape'][::-1]))
        rotated = [list(row) for row in rotated]
        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] = self.current_piece['color']
        # 检查是否有完整的行
        self.clear_lines()
        # 生成新方块
        self.current_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:
            self.score += lines_cleared * 10  # 改为每行10分
        

rotate_piece方法实现方块的旋转功能,通过矩阵转置和反转来实现。lock_piece方法将当前方块锁定在网格中,并检查是否有完整的行需要消除。clear_lines方法遍历所有行,找到并消除完整的行,同时更新分数。

游戏绘制方法


    def draw_controls(self):
        # 绘制控制说明
        controls = [
            "控制说明:",
            "← → : 左右移动",
            "↑ : 旋转",
            "↓ : 加速下落",
            "F : 水平翻转",
            "V : 垂直翻转",
            "空格 : 直接落到底部"
        ]
        for i, text in enumerate(controls):
            control_text = self.font.render(text, True, WHITE)
            self.screen.blit(control_text, 
                           (GAME_AREA_LEFT + GRID_WIDTH * CELL_SIZE + 10, 
                            80 + i * 30))

    def draw(self):
        self.screen.fill(BLACK)
        # 绘制游戏区域边框
        pygame.draw.rect(self.screen, WHITE, 
                        (GAME_AREA_LEFT - 2, 0, 
                        CELL_SIZE * GRID_WIDTH + 4, 
                        CELL_SIZE * GRID_HEIGHT + 4), 2)
        # 绘制网格
        for y in range(GRID_HEIGHT):
            for x in range(GRID_WIDTH):
                if self.grid[y][x]:
                    pygame.draw.rect(self.screen, self.grid[y][x], 
                                   (GAME_AREA_LEFT + x * CELL_SIZE, 
                                    y * CELL_SIZE, 
                                    CELL_SIZE, CELL_SIZE))
                    pygame.draw.rect(self.screen, WHITE, 
                                   (GAME_AREA_LEFT + x * CELL_SIZE, 
                                    y * CELL_SIZE, 
                                    CELL_SIZE, CELL_SIZE), 1)
        # 绘制当前方块
        for y, row in enumerate(self.current_piece['shape']):
            for x, cell in enumerate(row):
                if cell:
                    pygame.draw.rect(self.screen, self.current_piece['color'], 
                                   (GAME_AREA_LEFT + (self.current_piece['x'] + x) * CELL_SIZE, 
                                    (self.current_piece['y'] + y) * CELL_SIZE, 
                                    CELL_SIZE, CELL_SIZE))
                    pygame.draw.rect(self.screen, WHITE, 
                                   (GAME_AREA_LEFT + (self.current_piece['x'] + x) * CELL_SIZE, 
                                    (self.current_piece['y'] + y) * CELL_SIZE, 
                                    CELL_SIZE, CELL_SIZE), 1)
        # 绘制分数
        score_text = self.font.render(f'score: {self.score}', True, WHITE)
        self.screen.blit(score_text, (GAME_AREA_LEFT + GRID_WIDTH * CELL_SIZE + 10, 30))
        # 游戏结束提示
        if self.game_over:
            game_over_text = self.font.render('GAME OVER!', True, RED)
            self.screen.blit(game_over_text, (SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 - 15))
        # 绘制控制说明
        self.draw_controls()
        pygame.display.update()
        

draw_controls方法显示游戏控制说明,告诉玩家如何操作。draw方法负责渲染整个游戏界面,包括游戏区域边框、已锁定的方块、当前正在下落的方块、分数和游戏结束提示等。

游戏主循环


    def run(self):
        fall_time = 0
        fall_speed = 0.5  # 秒
        while not self.game_over:
            fall_time += self.clock.get_rawtime() / 1000
            self.clock.tick()
            
            # 方块自动下落
            if fall_time >= fall_speed:
                fall_time = 0
                if self.valid_move(self.current_piece, 0, 1):
                    self.current_piece['y'] += 1
                else:
                    self.lock_piece()
            
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.game_over = True
                
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        if self.valid_move(self.current_piece, -1, 0):
                            self.current_piece['x'] -= 1
                    elif event.key == pygame.K_RIGHT:
                        if self.valid_move(self.current_piece, 1, 0):
                            self.current_piece['x'] += 1
                    elif event.key == pygame.K_DOWN:
                        if self.valid_move(self.current_piece, 0, 1):
                            self.current_piece['y'] += 1
                    elif event.key == pygame.K_UP:
                        self.rotate_piece()
                    elif event.key == pygame.K_f:  # 水平翻转
                        self.flip_piece(horizontal=True)
                    elif event.key == pygame.K_v:  # 垂直翻转
                        self.flip_piece(horizontal=False)
                    elif event.key == pygame.K_SPACE:  # 硬降落
                        while self.valid_move(self.current_piece, 0, 1):
                            self.current_piece['y'] += 1
                        self.lock_piece()
            
            self.draw()
        pygame.quit()

if __name__ == "__main__":
    game = Tetris()
    game.run()
        

run方法是游戏的主循环,控制游戏的运行流程。它处理方块的自动下落、用户输入事件(如按键操作),并更新游戏状态和渲染画面,直到游戏结束。

游戏扩展与优化

这个俄罗斯方块游戏已经具备了基本功能,但还有很多可以扩展和优化的地方:

预览功能

添加下一个方块预览功能,让玩家提前知道接下来会出现什么方块

难度系统

实现游戏级别系统,随着分数增加提高方块下落速度

音效增强

添加音效和背景音乐,增强游戏体验

界面美化

设计更美观的界面和动画效果

暂停功能

实现游戏暂停和继续功能

高分榜

添加高分榜功能,记录玩家的最高分数

通过这个项目,你学习了如何使用Pygame开发一个完整的游戏,包括游戏循环、图形渲染、用户输入处理等核心概念。希望这篇文章对你理解游戏开发有所帮助!