Pygame第11课——实现经典打方块小游戏

在这里插入图片描述

专栏导读

  • 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手

  • 🏳️‍🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注

  • 👍 该系列文章专栏:请点击——>Python办公自动化专栏求订阅

  • 🕷 此外还有爬虫专栏:请点击——>Python爬虫基础专栏求订阅

  • 📕 此外还有python基础专栏:请点击——>Python基础学习专栏求订阅

  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏

  • ❤️ 欢迎各位佬关注! ❤️

前言

  • 在这个项目中,我将带你从头开始实现一个经典的打方块(Breakout)小游戏。这个游戏不仅能让你体验到编程的乐趣,还能学到游戏开发的核心概念。

🎮 游戏预览

这是一个基于 pygame 的经典打方块游戏,包含:
  • 彩色方块阵列(6行10列,每行不同颜色)
  • 可控制的挡板(左右方向键)
  • 物理反弹的小球
  • 分数系统和生命值
  • Game Over 与重新开始机制
  • 通关后自动进入下一关并提升难度

🏗️ 项目架构

为了让代码结构清晰、易于维护,我将项目分为三个核心模块:
打方块小游戏/
├── config.py      # 游戏配置与常量
├── entities.py    # 游戏实体类(挡板、小球、方块)
├── main.py        # 主游戏循环与逻辑
└── requirements.txt # 依赖管理

模块职责分工

  1. config.py - 集中管理所有配置参数
  2. entities.py - 定义游戏中的各种对象
  3. main.py - 处理游戏循环、事件、碰撞检测

🔧 技术实现详解

1. 配置管理 (config.py)

将所有游戏参数集中在配置文件中,便于调试和平衡性调整:
# 窗口设置
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
FPS = 60

# 挡板设置
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_SPEED = 8

# 小球设置
BALL_RADIUS = 10
BALL_SPEED_X = 3
BALL_SPEED_Y = -3

# 方块阵列设置
BRICK_ROWS = 6
BRICK_COLS = 10
BRICK_COLORS = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE]

这种设计的好处是:

  • 易于调试快速修改游戏参数无需深入代码
  • 平衡性调整轻松测试不同的难度设置
  • 可维护性避免魔法数字散布在代码各处

2. 游戏实体设计 (entities.py)

挡板类 (Paddle)

类实现了玩家控制的挡板:

class Paddle:
    def __init__(self):
        # 挡板初始位置:水平居中,距离底部40像素
        self.rect = Rect((WINDOW_WIDTH - PADDLE_WIDTH) // 2,
                         WINDOW_HEIGHT - 40,
                         PADDLE_WIDTH, PADDLE_HEIGHT)
    
    def move(self, dx):
        # 边界检测,确保挡板不会移出屏幕
        self.rect.x += dx * self.speed
        self.rect.x = max(0, min(self.rect.x, WINDOW_WIDTH - self.rect.width))

设计亮点

  • 使用 pygame.Rect 简化碰撞检测
  • 内置边界检测防止挡板移出屏幕
  • 支持变速移动(通过 self.speed 调节)
小球类 (Ball)

类是游戏的核心,实现了物理运动和墙壁碰撞:

def update(self):
    # 更新位置
    self.rect.x += self.vx
    self.rect.y += self.vy
    
    # 墙壁碰撞检测
    if self.rect.left <= 0 or self.rect.right >= WINDOW_WIDTH:
        self.vx *= -1  # 水平反弹
    if self.rect.top <= 0:
        self.vy *= -1  # 垂直反弹

物理模拟要点

  • 简单的欧拉积分法更新位置
  • 完美弹性碰撞(速度分量取反)
  • 边界检测确保小球不会穿墙
方块类与关卡生成

类设计简洁,重点在于状态管理:

class Brick:
    def __init__(self, x, y, width=BRICK_WIDTH, height=BRICK_HEIGHT, color=(200, 200, 200)):
        self.rect = Rect(x, y, width, height)
        self.color = color
        self.alive = True  # 核心状态:是否还存在

函数负责生成整齐排列的方块阵列:

def create_bricks():
    bricks = []
    total_width = BRICK_COLS * BRICK_WIDTH + (BRICK_COLS - 1) * BRICK_PADDING
    start_x = (WINDOW_WIDTH - total_width) // 2  # 水平居中
    
    for row in range(BRICK_ROWS):
        color = BRICK_COLORS[row % len(BRICK_COLORS)]  # 每行不同颜色
        for col in range(BRICK_COLS):
            x = start_x + col * (BRICK_WIDTH + BRICK_PADDING)
            y = BRICK_TOP_MARGIN + row * (BRICK_HEIGHT + BRICK_PADDING)
            bricks.append(Brick(x, y, BRICK_WIDTH, BRICK_HEIGHT, color))
    return bricks

设计精髓

  • 自动计算居中位置,适应不同屏幕尺寸
  • 颜色循环分配,创造视觉层次感
  • 规则网格布局,确保间距一致

3. 核心游戏逻辑 (main.py)

挡板-小球碰撞算法

函数实现了智能的挡板反弹:

def check_ball_paddle_collision(ball: Ball, paddle: Paddle):
    if ball.rect.colliderect(paddle.rect):
        if ball.vy > 0:  # 只在向下运动时反弹
            # 根据击中位置调整反弹角度
            offset = (ball.rect.centerx - paddle.rect.centerx) / (paddle.rect.width / 2)
            offset = max(-1, min(1, offset))  # 限制在 [-1, 1] 范围
            
            speed = abs(ball.vx) + abs(ball.vy)  # 保持总速度
            ball.vx = speed * offset
            ball.vy = -abs(ball.vy)
            
            # 防止穿透
            ball.rect.bottom = paddle.rect.top - 1

算法亮点

  • 角度控制根据击中位置改变反弹方向,增加策略性
  • 防粘连机制只在小球向下时才反弹,避免卡住
  • 速度保持维持总速度不变,确保游戏节奏
  • 穿透修正强制调整位置防止物理穿透
方块碰撞与方向判断

函数处理复杂的方块碰撞:

def check_ball_bricks_collision(ball: Ball, bricks):
    score_gain = 0
    for brick in bricks:
        if brick.alive and ball.rect.colliderect(brick.rect):
            brick.alive = False
            score_gain += SCORE_PER_BRICK
            
            # 智能碰撞方向判断
            dx_left = ball.rect.right - brick.rect.left
            dx_right = brick.rect.right - ball.rect.left
            dy_top = ball.rect.bottom - brick.rect.top
            dy_bottom = brick.rect.bottom - ball.rect.top
            
            min_dx = min(dx_left, dx_right)
            min_dy = min(dy_top, dy_bottom)
            
            if min_dx < min_dy:
                ball.vx *= -1  # 水平反弹
            else:
                ball.vy *= -1  # 垂直反弹
            break
    return score_gain

碰撞方向算法解析

  1. 计算小球与方块各边的重叠距离
  2. 比较水平和垂直方向的最小重叠
  3. 选择重叠更小的方向进行反弹
  4. 这样确保了物理上合理的反弹效果
主游戏循环架构

函数采用经典的游戏循环结构:

def main():
    # 初始化
    pygame.init()
    screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    clock = pygame.time.Clock()
    
    # 游戏对象
    paddle = Paddle()
    ball = Ball()
    bricks = create_bricks()
    
    while running:
        clock.tick(FPS)  # 控制帧率
        
        # 1. 处理输入
        for event in pygame.event.get():
            # 事件处理...
        
        # 2. 更新游戏状态
        ball.update()
        check_ball_paddle_collision(ball, paddle)
        score += check_ball_bricks_collision(ball, bricks)
        
        # 3. 渲染画面
        screen.fill(BLACK)
        paddle.draw(screen)
        ball.draw(screen)
        # ...
        
        pygame.display.flip()

游戏循环最佳实践

  • 固定时间步长使用 clock.tick(FPS) 确保稳定帧率
  • 清晰的阶段划分输入→更新→渲染,逻辑清晰
  • 状态管理生命值、分数、游戏状态统一管理

🎯 核心特性实现

1. 生命系统与游戏重置

if ball.rect.top > WINDOW_HEIGHT:
    lives -= 1
    if lives <= 0:
        # 游戏结束逻辑
        draw_text(screen, "Game Over - 按回车重新开始", 36, WHITE, ...)
        # 等待重新开始
        while wait_restart:
            # 事件监听...
        # 重置所有游戏状态
        paddle = Paddle()
        ball = Ball()
        bricks = create_bricks()
        score = 0
        lives = LIVES
    else:
        ball.reset()  # 重置小球位置

2. 难度递增机制

# 通关检测
if all(not b.alive for b in bricks):
    bricks = create_bricks()  # 重新生成方块
    # 提升难度
    ball.vx *= 1.1
    ball.vy *= 1.1

简单而有效的难度提升:每通过一关,小球速度增加10%。

3. 用户界面与反馈

def draw_text(surface, text, size, color, center):
    font = pygame.font.SysFont(None, size)
    img = font.render(text, True, color)
    rect = img.get_rect(center=center)
    surface.blit(img, rect)

# HUD 显示
draw_text(screen, f"Score: {score}", 28, WHITE, (80, 20))
draw_text(screen, f"Lives: {lives}", 28, WHITE, (WINDOW_WIDTH - 80, 20))

🚀 运行与安装

环境要求

  • Python 3.7+
  • pygame-ce(兼容新版本Python的pygame社区版)

快速开始

# 3. 安装依赖
pip install -r requirements.txt

# 4. 运行游戏
python main.py

操作说明

  • 左右方向键:移动挡板
  • ESC:退出游戏
  • Enter:游戏结束后重新开始

完整代码

  • config.py

# 游戏配置文件
import pygame

# 窗口设置
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
FPS = 60

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

# 挡板设置
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_SPEED = 8
PADDLE_COLOR = WHITE

# 小球设置
BALL_RADIUS = 10
BALL_SPEED_X = 3
BALL_SPEED_Y = -3
BALL_COLOR = WHITE

# 方块设置
BRICK_WIDTH = 75
BRICK_HEIGHT = 30
BRICK_PADDING = 5
BRICK_ROWS = 6
BRICK_COLS = 10
BRICK_TOP_MARGIN = 80

# 方块颜色 (每行不同颜色)
BRICK_COLORS = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE]

# 游戏设置
LIVES = 3
SCORE_PER_BRICK = 10
  • entities.py

import pygame
from pygame import Rect
from config import (
    WINDOW_WIDTH, WINDOW_HEIGHT,
    PADDLE_WIDTH, PADDLE_HEIGHT, PADDLE_SPEED, PADDLE_COLOR,
    BALL_RADIUS, BALL_SPEED_X, BALL_SPEED_Y, BALL_COLOR,
    BRICK_WIDTH, BRICK_HEIGHT, BRICK_PADDING, BRICK_ROWS, BRICK_COLS, BRICK_TOP_MARGIN, BRICK_COLORS
)

class Paddle:
    def __init__(self):
        self.rect = Rect((WINDOW_WIDTH - PADDLE_WIDTH) // 2,
                         WINDOW_HEIGHT - 40,
                         PADDLE_WIDTH, PADDLE_HEIGHT)
        self.speed = PADDLE_SPEED
        self.color = PADDLE_COLOR

    def move(self, dx):
        self.rect.x += dx * self.speed
        self.rect.x = max(0, min(self.rect.x, WINDOW_WIDTH - self.rect.width))

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, self.rect)

class Ball:
    def __init__(self):
        self.radius = BALL_RADIUS
        self.color = BALL_COLOR
        self.reset()

    def reset(self):
        self.rect = Rect(WINDOW_WIDTH // 2 - self.radius,
                         WINDOW_HEIGHT // 2 - self.radius,
                         self.radius * 2, self.radius * 2)
        self.vx = BALL_SPEED_X
        self.vy = BALL_SPEED_Y

    def update(self):
        self.rect.x += self.vx
        self.rect.y += self.vy

        # 碰撞墙壁
        if self.rect.left <= 0 or self.rect.right >= WINDOW_WIDTH:
            self.vx *= -1
        if self.rect.top <= 0:
            self.vy *= -1

    def draw(self, surface):
        pygame.draw.ellipse(surface, self.color, self.rect)

class Brick:
    def __init__(self, x, y, width=BRICK_WIDTH, height=BRICK_HEIGHT, color=(200, 200, 200)):
        self.rect = Rect(x, y, width, height)
        self.color = color
        self.alive = True

    def draw(self, surface):
        if self.alive:
            pygame.draw.rect(surface, self.color, self.rect)
            # 描边
            pygame.draw.rect(surface, (50, 50, 50), self.rect, 2)


def create_bricks():
    bricks = []
    total_width = BRICK_COLS * BRICK_WIDTH + (BRICK_COLS - 1) * BRICK_PADDING
    start_x = (WINDOW_WIDTH - total_width) // 2

    for row in range(BRICK_ROWS):
        color = BRICK_COLORS[row % len(BRICK_COLORS)]
        for col in range(BRICK_COLS):
            x = start_x + col * (BRICK_WIDTH + BRICK_PADDING)
            y = BRICK_TOP_MARGIN + row * (BRICK_HEIGHT + BRICK_PADDING)
            bricks.append(Brick(x, y, BRICK_WIDTH, BRICK_HEIGHT, color))
    return bricks
  • main.py

import sys
import pygame
from pygame.locals import QUIT, KEYDOWN, K_LEFT, K_RIGHT

from config import (
    WINDOW_WIDTH, WINDOW_HEIGHT, FPS,
    BLACK, WHITE, RED,
    LIVES, SCORE_PER_BRICK
)
from entities import Paddle, Ball, create_bricks


def check_ball_paddle_collision(ball: Ball, paddle: Paddle):
    if ball.rect.colliderect(paddle.rect):
        # 只在小球向下运动时反弹,避免粘连
        if ball.vy > 0:
            offset = (ball.rect.centerx - paddle.rect.centerx) / (paddle.rect.width / 2)
            offset = max(-1, min(1, offset))
            speed = abs(ball.vx) + abs(ball.vy)
            ball.vx = speed * offset
            ball.vy = -abs(ball.vy)
            # 调整位置避免穿透
            ball.rect.bottom = paddle.rect.top - 1


def check_ball_bricks_collision(ball: Ball, bricks):
    score_gain = 0
    for brick in bricks:
        if brick.alive and ball.rect.colliderect(brick.rect):
            brick.alive = False
            score_gain += SCORE_PER_BRICK

            # 碰撞方向判断
            dx_left = ball.rect.right - brick.rect.left
            dx_right = brick.rect.right - ball.rect.left
            dy_top = ball.rect.bottom - brick.rect.top
            dy_bottom = brick.rect.bottom - ball.rect.top
            min_dx = min(dx_left, dx_right)
            min_dy = min(dy_top, dy_bottom)
            if min_dx < min_dy:
                ball.vx *= -1
            else:
                ball.vy *= -1
            break
    return score_gain


def draw_text(surface, text, size, color, center):
    font = pygame.font.SysFont(None, size)
    img = font.render(text, True, color)
    rect = img.get_rect(center=center)
    surface.blit(img, rect)


def main():
    pygame.init()
    screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    pygame.display.set_caption("打方块小游戏")
    clock = pygame.time.Clock()

    paddle = Paddle()
    ball = Ball()
    bricks = create_bricks()

    score = 0
    lives = LIVES
    running = True

    while running:
        clock.tick(FPS)
        # 事件处理
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
            elif event.type == KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False

        # 键盘连续输入
        keys = pygame.key.get_pressed()
        dx = (keys[K_RIGHT] - keys[K_LEFT])
        if dx != 0:
            paddle.move(dx)

        # 更新小球
        ball.update()

        # 碰撞检测
        check_ball_paddle_collision(ball, paddle)
        score += check_ball_bricks_collision(ball, bricks)

        # 掉出底部
        if ball.rect.top > WINDOW_HEIGHT:
            lives -= 1
            if lives <= 0:
                # 游戏结束,重置
                draw_text(screen, "Game Over - 按回车重新开始", 36, WHITE, (WINDOW_WIDTH//2, WINDOW_HEIGHT//2))
                pygame.display.flip()
                wait_restart = True
                while wait_restart:
                    for event in pygame.event.get():
                        if event.type == QUIT:
                            pygame.quit()
                            sys.exit()
                        elif event.type == KEYDOWN and event.key == pygame.K_RETURN:
                            wait_restart = False
                # 重置游戏
                paddle = Paddle()
                ball = Ball()
                bricks = create_bricks()
                score = 0
                lives = LIVES
            else:
                ball.reset()

        # 全部方块清空,进入下一关(简单处理:重新生成方块,速度稍增)
        if all(not b.alive for b in bricks):
            bricks = create_bricks()
            # 提升难度
            ball.vx *= 1.1
            ball.vy *= 1.1

        # 绘制
        screen.fill(BLACK)
        paddle.draw(screen)
        ball.draw(screen)
        for brick in bricks:
            brick.draw(screen)

        # HUD
        draw_text(screen, f"Score: {score}", 28, WHITE, (80, 20))
        draw_text(screen, f"Lives: {lives}", 28, WHITE, (WINDOW_WIDTH - 80, 20))

        pygame.display.flip()

    pygame.quit()


if __name__ == "__main__":
    main()

🎨 扩展思路

这个基础版本为进一步扩展奠定了良好基础:

游戏性扩展

  • 道具系统加长挡板、多球、穿透球等
  • 特殊方块需要多次击打、爆炸方块、移动方块
  • 关卡编辑器自定义方块布局和颜色

技术优化

  • 粒子效果方块破碎、小球轨迹特效
  • 音效系统碰撞音效、背景音乐
  • 存档系统最高分记录、关卡进度保存

平台适配

  • 移动端适配触摸控制、屏幕适配
  • Web版本使用 Pygame Web 或 Panda3D
  • 多人模式本地或网络对战

📚 学习收获

通过这个项目,你将掌握:

  1. 游戏开发基础游戏循环、状态管理、碰撞检测
  2. 面向对象设计类的设计与交互、模块化编程
  3. 物理模拟简单的运动学、弹性碰撞
  4. 用户交互键盘输入处理、实时反馈
  5. 项目结构配置管理、代码组织、依赖管理

🏆 结语

这个打方块游戏虽然简单,但包含了游戏开发的核心要素。从配置管理到物理模拟,从碰撞检测到用户界面,每一个部分都值得深入研究。
更重要的是,这个项目展示了如何用清晰的架构和合理的设计模式来构建一个完整的应用程序。无论你是游戏开发新手还是想要回顾经典,这个项目都能给你带来启发。
现在就开始游戏,看看你能坚持多少关吧!🎮

  • 希望对初学者有帮助;致力于办公自动化的小小程序员一枚

  • 希望能得到大家的【❤️一个免费关注❤️】感谢!

  • 求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍

  • 此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏

  • 此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏

  • 此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小庄-Python办公

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值