柏虎资源网

专注编程学习,Python、Java、C++ 教程、案例及资源

用python做一个打砖块游戏,源码有详细注释

打砖块游戏开发思路总结




着急看源码的同学,请直接拉到最后!

项目概述

这是一个使用Python和Pygame库开发的经典打砖块游戏,适合初学者学习游戏开发的基本概念和面向对象编程。

核心设计思路

1. 面向对象设计

游戏采用面向对象的设计模式,将不同的游戏元素封装成独立的类:

  • Paddle类(挡板): 负责玩家控制的挡板逻辑
  • Ball类(球): 负责弹球的移动和碰撞逻辑
  • Brick类(砖块): 负责可被击碎的砖块
  • Game类(游戏主控制器): 负责整体游戏逻辑和状态管理

学习要点: 这种设计让代码结构清晰,每个类都有明确的职责,便于维护和扩展。

2. 游戏循环架构

游戏采用经典的游戏循环模式:

初始化 → 游戏循环(事件处理 → 状态更新 → 画面绘制)→ 清理资源

学习要点: 这是所有实时游戏的基本架构,理解这个循环对游戏开发至关重要。

3. 碰撞检测系统

游戏实现了多种碰撞检测:

  • 球与墙壁的碰撞(边界检测)
  • 球与挡板的碰撞(矩形碰撞检测)
  • 球与砖块的碰撞(矩形碰撞检测)

学习要点: Pygame的colliderect()方法简化了矩形碰撞检测,这是2D游戏中最常用的碰撞检测方法。

技术实现细节

1. 坐标系统

  • 使用Pygame的坐标系统(左上角为原点(0,0))
  • X轴向右为正,Y轴向下为正
  • 所有游戏对象都使用pygame.Rect来管理位置和大小

2. 物理模拟

  • 球的运动:简单的线性运动,通过改变速度向量实现反弹
  • 反弹逻辑:撞击时将对应方向的速度分量取反
  • 挡板交互:根据球撞击挡板的位置调整反弹角度
  • 动态速度调整:根据游戏进度自动增加球的移动速度
  • 渐进式难度:通过分批生成砖块控制游戏节奏

3. 游戏状态管理

  • 使用布尔值和条件判断管理游戏状态
  • 实现游戏结束检测(球掉落或所有砖块被摧毁)
  • 提供重新开始功能
  • 添加了动态难度调整系统
  • 实现了砖块的分批生成机制

代码组织结构

1. 常量定义

# 将所有游戏参数定义为常量,便于调整和维护
WIDTH = 800  # 窗口宽度
HEIGHT = 600  # 窗口高度
FPS = 60  # 帧率

2. 类的设计原则

  • 单一职责原则: 每个类只负责一个特定功能
  • 封装性: 将数据和操作数据的方法封装在一起
  • 可扩展性: 设计时考虑了后续功能扩展的可能性

3. 方法命名规范

  • 使用动词开头的方法名(如move(), draw(), update())
  • 使用描述性的方法名(如check_wall_collision())
  • 保持命名的一致性

学习重点和扩展建议

初学者重点关注

  1. Pygame基础: 窗口创建、事件处理、图形绘制
  2. 面向对象编程: 类的定义、实例化、方法调用
  3. 游戏循环: 理解游戏的基本运行机制
  4. 碰撞检测: 学习基本的碰撞检测原理

最新功能更新

  1. 可调节速度系统:
  2. 降低了初始球速,让新手更容易上手
  3. 每摧毁10个砖块(得分100分)自动增加速度等级
  4. 实时显示当前速度等级
  5. 动态砖块生成:
  6. 游戏开始时只显示前3行砖块
  7. 每3秒(180帧)自动生成新的一行砖块
  8. 最多生成5行砖块,避免屏幕过于拥挤
  9. 优化用户界面:
  10. 修复了中文显示问题
  11. 简化了操作提示文字
  12. 添加了速度等级显示

进阶学习方向

  1. 添加音效: 使用pygame.mixer添加背景音乐和音效
  2. 粒子效果: 砖块被击碎时添加粒子爆炸效果
  3. 关卡系统: 设计多个关卡,增加游戏难度
  4. 道具系统: 添加各种道具(如多球、加长挡板等)
  5. AI对手: 实现电脑控制的挡板

代码优化建议

  1. 配置文件: 将游戏参数移到配置文件中
  2. 资源管理: 实现图片和音频资源的统一管理
  3. 状态机: 使用状态机模式管理游戏的不同状态
  4. 组件系统: 采用ECS(Entity-Component-System)架构

常见问题和解决方案

1. 性能优化

  • 避免在游戏循环中创建新对象
  • 合理使用pygame.sprite.Group进行批量操作
  • 只绘制屏幕可见区域的对象

2. 碰撞检测精度

  • 当前使用矩形碰撞检测,对于圆形球可能不够精确
  • 可以考虑使用圆形碰撞检测提高精度

3. 游戏平衡性

  • 球速、挡板速度、砖块布局都会影响游戏难度
  • 需要通过测试找到合适的参数平衡

总结

这个打砖块游戏项目涵盖了游戏开发的核心概念:

  • 面向对象编程
  • 游戏循环
  • 事件处理
  • 碰撞检测
  • 图形渲染
  • 状态管理

通过学习和修改这个项目,可以掌握游戏开发的基本技能,为开发更复杂的游戏打下坚实基础。建议在理解现有代码的基础上,尝试添加新功能来加深理解。


源码及解析:

import pygame
import sys
import random

# 初始化pygame
pygame.init()

# 游戏常量设置
WIDTH = 800  # 游戏窗口宽度
HEIGHT = 600  # 游戏窗口高度
FPS = 60  # 游戏帧率

# 颜色定义 (RGB格式)
WHITE = (255, 255, 255)  # 白色
BLACK = (0, 0, 0)  # 黑色
RED = (255, 0, 0)  # 红色
GREEN = (0, 255, 0)  # 绿色
BLUE = (0, 0, 255)  # 蓝色
YELLOW = (255, 255, 0)  # 黄色
ORANGE = (255, 165, 0)  # 橙色
PURPLE = (128, 0, 128)  # 紫色

# 挡板相关常量
PADDLE_WIDTH = 100  # 挡板宽度
PADDLE_HEIGHT = 10  # 挡板高度
PADDLE_SPEED = 8  # 挡板移动速度

# 球相关常量
BALL_SIZE = 15  # 球的大小(半径)
BALL_SPEED_X = 3  # 球的初始水平速度(降低起始速度)
BALL_SPEED_Y = -3  # 球的初始垂直速度(负数表示向上,降低起始速度)
SPEED_INCREMENT = 0.2  # 速度增长幅度

# 砖块相关常量
BRICK_WIDTH = 75  # 砖块宽度
BRICK_HEIGHT = 20  # 砖块高度
BRICK_ROWS = 5  # 砖块行数(减少初始砖块数量)
BRICK_COLS = 10  # 砖块列数
BRICK_PADDING = 5  # 砖块间距
BRICK_SPAWN_INTERVAL = 180  # 新砖块生成间隔(帧数)

class Paddle:
    """挡板类 - 负责玩家控制的挡板"""
    
    def __init__(self, x, y):
        """初始化挡板
        Args:
            x: 挡板初始x坐标
            y: 挡板初始y坐标
        """
        self.rect = pygame.Rect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT)  # 创建挡板矩形
        self.speed = PADDLE_SPEED  # 设置挡板移动速度
    
    def move_left(self):
        """向左移动挡板"""
        self.rect.x -= self.speed  # 减少x坐标实现左移
        # 确保挡板不会移出屏幕左边界
        if self.rect.x < 0:
            self.rect.x = 0
    
    def move_right(self):
        """向右移动挡板"""
        self.rect.x += self.speed  # 增加x坐标实现右移
        # 确保挡板不会移出屏幕右边界
        if self.rect.x > WIDTH - PADDLE_WIDTH:
            self.rect.x = WIDTH - PADDLE_WIDTH
    
    def draw(self, screen):
        """在屏幕上绘制挡板
        Args:
            screen: pygame屏幕对象
        """
        pygame.draw.rect(screen, WHITE, self.rect)  # 绘制白色矩形挡板

class Ball:
    """球类 - 负责游戏中的弹球"""
    
    def __init__(self, x, y):
        """初始化球
        Args:
            x: 球的初始x坐标
            y: 球的初始y坐标
        """
        self.rect = pygame.Rect(x, y, BALL_SIZE * 2, BALL_SIZE * 2)  # 创建球的矩形(用于碰撞检测)
        self.speed_x = BALL_SPEED_X  # 球的水平速度
        self.speed_y = BALL_SPEED_Y  # 球的垂直速度
        self.radius = BALL_SIZE  # 球的半径
    
    def move(self):
        """移动球的位置"""
        self.rect.x += self.speed_x  # 根据水平速度更新x坐标
        self.rect.y += self.speed_y  # 根据垂直速度更新y坐标
    
    def bounce_x(self):
        """水平方向反弹(撞到左右边界时)"""
        self.speed_x = -self.speed_x  # 水平速度取反
    
    def bounce_y(self):
        """垂直方向反弹(撞到上下边界或挡板时)"""
        self.speed_y = -self.speed_y  # 垂直速度取反
    
    def check_wall_collision(self):
        """检查球与墙壁的碰撞
        Returns:
            bool: 如果球掉到底部返回True(游戏结束条件)
        """
        # 检查左右边界碰撞
        if self.rect.x <= 0 or self.rect.x >= WIDTH - BALL_SIZE * 2:
            self.bounce_x()  # 水平反弹
        
        # 检查上边界碰撞
        if self.rect.y <= 0:
            self.bounce_y()  # 垂直反弹
        
        # 检查下边界碰撞(球掉落)
        if self.rect.y >= HEIGHT:
            return True  # 返回True表示游戏结束
        
        return False  # 返回False表示游戏继续
    
    def draw(self, screen):
        """在屏幕上绘制球
        Args:
            screen: pygame屏幕对象
        """
        # 绘制白色圆形球,中心坐标为矩形中心
        pygame.draw.circle(screen, WHITE, 
                         (self.rect.x + self.radius, self.rect.y + self.radius), 
                         self.radius)

class Brick:
    """砖块类 - 负责可被击碎的砖块"""
    
    def __init__(self, x, y, color):
        """初始化砖块
        Args:
            x: 砖块x坐标
            y: 砖块y坐标
            color: 砖块颜色
        """
        self.rect = pygame.Rect(x, y, BRICK_WIDTH, BRICK_HEIGHT)  # 创建砖块矩形
        self.color = color  # 设置砖块颜色
        self.destroyed = False  # 砖块是否被摧毁的标志
    
    def draw(self, screen):
        """在屏幕上绘制砖块
        Args:
            screen: pygame屏幕对象
        """
        if not self.destroyed:  # 只有未被摧毁的砖块才绘制
            pygame.draw.rect(screen, self.color, self.rect)  # 绘制彩色矩形砖块
            pygame.draw.rect(screen, BLACK, self.rect, 2)  # 绘制黑色边框

class Game:
    """游戏主类 - 负责游戏逻辑和状态管理"""
    
    def __init__(self):
        """初始化游戏"""
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))  # 创建游戏窗口
        pygame.display.set_caption("打砖块游戏 - Breakout")  # 设置窗口标题
        self.clock = pygame.time.Clock()  # 创建时钟对象控制帧率
        
        # 初始化游戏对象
        self.paddle = Paddle(WIDTH // 2 - PADDLE_WIDTH // 2, HEIGHT - 50)  # 在屏幕底部中央创建挡板
        self.ball = Ball(WIDTH // 2 - BALL_SIZE, HEIGHT // 2)  # 在屏幕中央创建球
        self.bricks = []  # 砖块列表
        self.score = 0  # 玩家得分
        # 尝试使用系统中文字体,如果没有则使用默认字体
        try:
            self.font = pygame.font.Font("C:/Windows/Fonts/simhei.ttf", 36)  # 黑体
        except:
            try:
                self.font = pygame.font.Font("C:/Windows/Fonts/msyh.ttc", 36)  # 微软雅黑
            except:
                self.font = pygame.font.SysFont("SimHei,Microsoft YaHei,Arial", 36)  # 系统字体
        self.spawn_timer = 0  # 砖块生成计时器
        self.current_row = 0  # 当前生成到第几行
        self.speed_level = 1  # 速度等级
        
        self.create_initial_bricks()  # 创建初始砖块
    
    def create_initial_bricks(self):
        """创建初始砖块阵列(只创建前3行)"""
        colors = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE]  # 定义砖块颜色数组
        
        # 只创建前3行砖块
        for row in range(3):
            for col in range(BRICK_COLS):
                # 计算每个砖块的位置
                x = col * (BRICK_WIDTH + BRICK_PADDING) + BRICK_PADDING
                y = row * (BRICK_HEIGHT + BRICK_PADDING) + 50  # 从顶部50像素开始
                
                # 根据行数选择颜色,创建彩虹效果
                color = colors[row % len(colors)]
                
                # 创建砖块并添加到列表
                brick = Brick(x, y, color)
                self.bricks.append(brick)
        
        self.current_row = 3  # 设置当前已生成到第3行
    
    def spawn_new_brick_row(self):
        """生成新的砖块行"""
        if self.current_row >= BRICK_ROWS:
            return  # 如果已经生成了所有行,则不再生成
        
        colors = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE]
        
        # 生成新的一行砖块
        for col in range(BRICK_COLS):
            x = col * (BRICK_WIDTH + BRICK_PADDING) + BRICK_PADDING
            y = self.current_row * (BRICK_HEIGHT + BRICK_PADDING) + 50
            
            color = colors[self.current_row % len(colors)]
            brick = Brick(x, y, color)
            self.bricks.append(brick)
        
        self.current_row += 1  # 移动到下一行
    
    def increase_speed(self):
        """增加球的速度"""
        if self.speed_level < 10:  # 限制最大速度等级
            self.speed_level += 1
            # 增加球的速度,但保持方向
            if self.ball.speed_x > 0:
                self.ball.speed_x += SPEED_INCREMENT
            else:
                self.ball.speed_x -= SPEED_INCREMENT
            
            if self.ball.speed_y > 0:
                self.ball.speed_y += SPEED_INCREMENT
            else:
                self.ball.speed_y -= SPEED_INCREMENT
    
    def decrease_speed(self):
        """降低球的速度"""
        if self.speed_level > 1:  # 限制最小速度等级
            self.speed_level -= 1
            # 降低球的速度,但保持方向
            if self.ball.speed_x > 0:
                self.ball.speed_x -= SPEED_INCREMENT
            else:
                self.ball.speed_x += SPEED_INCREMENT
            
            if self.ball.speed_y > 0:
                self.ball.speed_y -= SPEED_INCREMENT
            else:
                self.ball.speed_y += SPEED_INCREMENT
    
    def handle_events(self):
        """处理游戏事件
        Returns:
            bool: 如果用户要求退出返回False
        """
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 用户点击关闭按钮
                return False
        
        # 获取当前按键状态
        keys = pygame.key.get_pressed()
        
        # 根据按键移动挡板
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:  # 左箭头键或A键
            self.paddle.move_left()
        if keys[pygame.K_RIGHT] or keys[pygame.K_d]:  # 右箭头键或D键
            self.paddle.move_right()
        
        # 手动调节速度
        if keys[pygame.K_MINUS]:  # -键降低速度
            self.decrease_speed()
        if keys[pygame.K_PLUS] or keys[pygame.K_EQUALS]:  # +键或=键增加速度
            self.increase_speed()
        
        return True  # 继续游戏
    
    def update(self):
        """更新游戏状态
        Returns:
            bool: 如果游戏结束返回False
        """
        # 移动球
        self.ball.move()
        
        # 检查球与墙壁碰撞
        if self.ball.check_wall_collision():
            return False  # 球掉落,游戏结束
        
        # 检查球与挡板碰撞
        if self.ball.rect.colliderect(self.paddle.rect):
            self.ball.bounce_y()  # 球向上反弹
            
            # 根据球撞击挡板的位置调整反弹角度
            hit_pos = (self.ball.rect.centerx - self.paddle.rect.centerx) / (PADDLE_WIDTH / 2)
            self.ball.speed_x = int(hit_pos * 3)  # 调整水平速度创造角度变化
        
        # 检查球与砖块碰撞
        bricks_destroyed_this_frame = 0
        for brick in self.bricks:
            if not brick.destroyed and self.ball.rect.colliderect(brick.rect):
                brick.destroyed = True  # 标记砖块为已摧毁
                self.ball.bounce_y()  # 球反弹
                self.score += 10  # 增加得分
                bricks_destroyed_this_frame += 1
                
                # 每摧毁10个砖块增加一次速度
                if self.score % 100 == 0:
                    self.increase_speed()
                
                break  # 一次只处理一个砖块碰撞
        
        # 砖块生成逻辑
        self.spawn_timer += 1
        if self.spawn_timer >= BRICK_SPAWN_INTERVAL:
            self.spawn_new_brick_row()
            self.spawn_timer = 0
        
        # 检查是否所有砖块都被摧毁(胜利条件)
        active_bricks = [brick for brick in self.bricks if not brick.destroyed]
        if len(active_bricks) == 0 and self.current_row >= BRICK_ROWS:
            return False  # 游戏胜利,结束游戏
        
        return True  # 游戏继续
    
    def draw(self):
        """绘制游戏画面"""
        self.screen.fill(BLACK)  # 用黑色填充屏幕背景
        
        # 绘制游戏对象
        self.paddle.draw(self.screen)  # 绘制挡板
        self.ball.draw(self.screen)  # 绘制球
        
        # 绘制所有砖块
        for brick in self.bricks:
            brick.draw(self.screen)
        
        # 绘制得分和速度等级(在最顶行水平排列)
        score_text = self.font.render(f"得分: {self.score}", True, WHITE)
        score_rect = score_text.get_rect(center=(WIDTH - 380, 20))  # 顶部右侧,再次向左移动
        self.screen.blit(score_text, score_rect)
        
        speed_text = self.font.render(f"速度等级: {self.speed_level}", True, WHITE)
        speed_rect = speed_text.get_rect(center=(WIDTH - 160, 20))  # 同一行,也向左移动
        self.screen.blit(speed_text, speed_rect)
        
        # 绘制操作提示(移到左下角)
        hint_text = self.font.render("方向键/AD: 移动 | -/+: 速度调节", True, WHITE)
        self.screen.blit(hint_text, (10, HEIGHT - 40))  # 在左下角显示操作提示
        
        pygame.display.flip()  # 更新显示
    
    def show_game_over(self, won=False):
        """显示游戏结束画面
        Args:
            won: 是否胜利
        """
        self.screen.fill(BLACK)  # 清空屏幕
        
        # 根据胜负显示不同信息
        if won:
            message = "恭喜!你赢了!"
            color = GREEN
        else:
            message = "游戏结束!"
            color = RED
        
        # 显示游戏结果
        game_over_text = self.font.render(message, True, color)
        text_rect = game_over_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 50))
        self.screen.blit(game_over_text, text_rect)
        
        # 显示最终得分
        score_text = self.font.render(f"最终得分: {self.score}", True, WHITE)
        score_rect = score_text.get_rect(center=(WIDTH // 2, HEIGHT // 2))
        self.screen.blit(score_text, score_rect)
        
        # 显示重新开始提示
        restart_text = self.font.render("按R键重新开始,按ESC键退出", True, WHITE)
        restart_rect = restart_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
        self.screen.blit(restart_text, restart_rect)
        
        pygame.display.flip()  # 更新显示
    
    def wait_for_restart(self):
        """等待玩家选择重新开始或退出
        Returns:
            bool: True表示重新开始,False表示退出
        """
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_r:  # R键重新开始
                        return True
                    elif event.key == pygame.K_ESCAPE:  # ESC键退出
                        return False
    
    def reset_game(self):
        """重置游戏状态"""
        # 重新创建游戏对象
        self.paddle = Paddle(WIDTH // 2 - PADDLE_WIDTH // 2, HEIGHT - 50)
        self.ball = Ball(WIDTH // 2 - BALL_SIZE, HEIGHT // 2)
        self.bricks = []
        self.score = 0
        self.spawn_timer = 0
        self.current_row = 0
        self.speed_level = 1
        self.create_initial_bricks()  # 重新创建初始砖块
    
    def run(self):
        """运行游戏主循环"""
        running = True
        
        while running:
            # 处理事件
            if not self.handle_events():
                running = False
                continue
            
            # 更新游戏状态
            game_continues = self.update()
            
            # 绘制游戏画面
            self.draw()
            
            # 如果游戏结束
            if not game_continues:
                # 检查是否胜利(所有砖块被摧毁)
                won = all(brick.destroyed for brick in self.bricks)
                
                # 显示游戏结束画面
                self.show_game_over(won)
                
                # 等待玩家选择
                if self.wait_for_restart():
                    self.reset_game()  # 重新开始游戏
                else:
                    running = False  # 退出游戏
            
            # 控制帧率
            self.clock.tick(FPS)
        
        # 退出pygame
        pygame.quit()
        sys.exit()

# 程序入口点
if __name__ == "__main__":
    # 创建游戏实例并运行
    game = Game()
    game.run()

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言