ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

brickout-game:Pygame 打砖块游戏的完整实现解析(Ball/Paddle/BrickWall 架构与碰撞检测)

brickout-game:Pygame 打砖块游戏的完整实现解析(Ball/Paddle/BrickWall 架构与碰撞检测) brickout-gamePygame 打砖块游戏的完整实现解析Ball/Paddle/BrickWall 架构与碰撞检测【免费下载链接】PythonMy Python Examples项目地址: https://gitcode.com/gh_mirrors/py/Python本指南以仓库 brickout-game/README.md 为骨架结合完整源码 brickout-game/brickout-game.py 逐层剖析一个用 Pygame 实现的经典打砖块Brickout小游戏它如何用Ball、Paddle、Brick、BrickWall四个类组织对象模型如何实现球与屏幕边界、球与砖块墙、球与挡板三套碰撞检测以及如何完成计分、胜负判定与 60 FPS 主循环。读完本文你可以掌握 Pygame 小游戏从对象建模、碰撞处理到主循环驱动的最小可运行套路并能在此基础上自由扩展玩法。项目概览一个 60 FPS 的经典打砖块游戏按照 README 的描述这是一个简单但功能完整的打砖块游戏核心要素包括Ball球在屏幕内移动、反弹的弹球Paddle挡板由玩家控制的反弹平台Brick Wall砖块墙由多块砖组成的障碍阵列碰撞检测上述对象之间的碰撞判定胜负逻辑球落出屏幕即失败Game Over清空全部砖块即胜利You win!计分系统击中砖块获得分数60 FPS游戏以每秒 60 帧运行Python 2README 明确说明代码基于 Python 2 编写。README 还交代了项目背景游戏主循环的基本结构参考了 programarcadegames.com 的 Pygame 入门样例该站提供完整的 Python/Pygame 入门教程与大量示例打砖块游戏逻辑与Ball、Paddle、Brick、BrickWall类由作者 Christian Bender 完成。这一点在源码文件头部的注释中也有记录。从仓库文件结构看整个游戏只有两个文件说明文档 brickout-game/README.md 与全部实现 brickout-game/brickout-game.py另有一张用于说明架构的类图 brickout-game/classDiagram.png。代码量小、依赖单一仅pygame非常适合作为 Pygame 入门与对象建模的教学样本。快速开始运行环境与启动命令README 给出的启动命令为python game.py。需要说明的是仓库中实际的脚本文件名是brickout-game.py不存在game.py因此在本仓库环境下应执行python brickout-game.py运行前提Python 环境README 注明代码以 Python 2 为目标。如果你使用 Python 3 运行大部分代码可以正常工作源码中使用了浮点除法width / 7.0、object基类等两种版本兼容写法但建议以 README 声明的 Python 2 环境为准遇到兼容性问题时优先检查版本差异。安装 pygame游戏依赖pygame需先安装pip install pygame启动后游戏会打开一个700×500像素的窗口见 brickout-game/brickout-game.py标题为 Brickout-game。游戏帧率由主循环末尾的clock.tick(60)限制在 60 FPS见 brickout-game/brickout-game.py。类图总览四大核心类的职责划分仓库中的 brickout-game/classDiagram.png 用一张类图直观概括了游戏的对象模型与源码实现一一对应结合源码可以这样理解这张图BrickWallGroup组继承自pygame.sprite.Group内部持有_bricks列表通过组合关系管理多块Brick整体—部分关系负责砖块的批量生成、批量绘制、碰撞检测与移除Brick砖块继承自pygame.sprite.Sprite是砖块墙的组成单元自带单砖碰撞判定collide方法Ball球游戏中的交互中心update方法接收paddle与brickwall两个参数完成移动、边界反弹并分发给挡板与砖块墙做碰撞判定对应图中 Ball 与 Brick、Ball 与 Paddle 的关联关系Paddle挡板由鼠标控制位置的矩形平台接收球的反弹。这样的分层让球—砖块球—挡板的交互通过BrickWall/Paddle暴露的接口完成而不是让球直接操作砖块列表保持了较好的职责边界。深入源码核心类的设计与实现Ball移动、屏幕边界反弹与碰撞分发Ball类的完整定义见 brickout-game/brickout-game.py。实例化时接收screen、半径radius与初始坐标ball Ball(screen, 25, random.randint(1, 700), 250)即球的半径是25 像素初始横坐标在 1~700 之间随机取值纵坐标固定为 250屏幕中部。球的速度写死在构造方法中水平速度__xVel 7垂直速度__yVel 2见 brickout-game/brickout-game.py因此在默认参数下球的运动轨迹是横向快、纵向慢的斜线。update(paddle, brickwall)是球的核心逻辑依次完成位移每帧按(xVel, yVel)更新坐标左右墙反弹当xLoc radius或xLoc width - radius时翻转水平速度xVel * -1顶部反弹当yLoc radius时翻转垂直速度底部掉出判定当yLoc超过一定阈值时返回True作为球丢失、游戏结束的信号该阈值判定在实现细节一节有进一步讨论砖块碰撞调用brickwall.collide(self)若命中则翻转垂直速度挡板碰撞读取paddle的坐标与尺寸做矩形相交判定命中则翻转垂直速度。注意该类的绘制使用全局screen变量调用pygame.draw.circle画红色圆而构造时传入的self.__screen在绘制中并未被使用见 brickout-game/brickout-game.py这是源码中一个有趣的实现特点后文会统一说明。Paddle鼠标驱动的挡板Paddle类见 brickout-game/brickout-game.py实例化参数为Paddle(screen, 100, 20, 250, 450)即宽100 像素、高20 像素初始位置 (250, 450)靠近屏幕底部。与多数打砖块游戏用键盘左右键控制不同这里的挡板完全由鼠标驱动def update(self): x, y pygame.mouse.get_pos() if x 0 and x (self.__W - self._width): self._xLoc x即每帧读取鼠标横坐标只要鼠标位于屏幕宽度减去挡板宽度的合法区间内就把挡板的_xLoc设为鼠标横坐标。玩家移动鼠标挡板便水平跟随。绘制时同样使用全局screen画一个黑色矩形brickout-game/brickout-game.py。Brick 与 BrickWall砖块的生成、命中与移除Brickbrickout-game/brickout-game.py继承pygame.sprite.Sprite绘制为RGB(56, 177, 237) 的蓝色矩形注释里标注的官方色值见 brickout-game/brickout-game.py。它通过add(group)/remove(group)维护自己的存活状态_isInGroup并提供单砖碰撞判定collide(ball)。BrickWallbrickout-game/brickout-game.py继承pygame.sprite.Group构造时一次性生成砖块阵列brickWall BrickWall(screen, 25, 25, 150, 50)参数含义砖块墙起点 (25, 25)每块砖宽150 像素、高50 像素。构造方法用双重循环铺出3 行 × 4 列 12 块砖行内横向间距为width width / 7.0约 171.4 像素行间距为height height / 7.0约 57.1 像素见 brickout-game/brickout-game.py。BrickWall对外提供四个关键方法draw()遍历_bricks列表逐个绘制update(ball)遍历砖块做碰撞检测命中的砖块先置为None随后循环移除所有None元素hasWin()len(self._bricks) 0时返回True即砖块全部清空即为胜利collide(ball)返回是否与任一砖块碰撞供球与主循环使用。碰撞检测的三种场景与实现细节整个游戏的碰撞检测只有三种全部采用轴对齐包围盒AABB思想用矩形相交判断完成没有引入物理引擎1. 球与屏幕边界在Ball.update中直接以坐标和半径比较xLoc radius碰到左墙xVel取反xLoc width - radius碰到右墙xVel取反yLoc radius碰到顶墙yVel取反yLoc width - radius判定为球掉出底部返回True触发游戏结束。2. 球与砖块单砖判定Brick.collide(ball)brickout-game/brickout-game.py的条件是球的右侧点(ballX radius)落入砖块横向区间[brickX, brickX brickW]且球的顶部点(ballY - radius)落入砖块纵向区间[brickY, brickY brickH]同时成立即视为命中。命中后由BrickWall.update移除该砖并由Ball.update将球的垂直速度取反形成撞砖反弹、砖块消失的效果。3. 球与挡板在Ball.update中直接读取挡板的公开属性做矩形相交判断brickout-game/brickout-game.pyif ((ballX self._radius) paddleX and ballX (paddleX paddleW)) and ( (ballY self._radius) paddleY and ballY (paddleY paddleH) ): self.__yVel * -1即球的右侧不越过挡板左缘、球的左侧不越过挡板右缘、球底不越过挡板上缘、球顶不越过挡板下缘四条件同时满足即反弹。主循环绘制、计分、胜负判定主循环位于 brickout-game/brickout-game.py是标准的 Pygame 三阶段结构事件处理pygame.event.get()中仅监听QUIT事件点击窗口关闭按钮即退出清屏与绘制screen.fill(WHITE)清为白色背景后按砖块墙 → 挡板 → 球的顺序绘制状态更新依次执行brickWall.update(ball)、paddle.update()、ball.update(paddle, brickWall)翻页与限帧pygame.display.flip()刷新画面clock.tick(60)将帧率锁定为 60 FPS。游戏状态由两个布尔变量驱动gameStatus游戏是否仍在进行与isGameOver是否失败全局计分变量为scorebrickout-game/brickout-game.py。计分规则每击中一块砖 10 分计分逻辑位于 brickout-game/brickout-game.pyif brickWall.collide(ball): score 10主循环在移除命中砖块brickWall.update(ball)之前先检测球是否与砖块墙相交命中一次加 10 分。由于碰撞检测按帧进行、球横穿一块 150 像素宽的砖需要若干帧实际运行时同一块砖在消失前可能被累计多次 10即命中一块砖通常不止加 10 分这是源码实现中的一个可观察行为后续扩展时若要严格一砖 10 分可以改为在砖块移除时计分。分数以 score: N 的文本形式用Comic Sans MS40 号字体绘制在屏幕左上区域screen.blit(textsurfaceScore, (300, 0))。胜负判定掉球失败清砖胜利失败ball.update(paddle, brickWall)返回True球掉出底部时isGameOver True、gameStatus False画面显示黑色 Game Over! 文本与最终分数brickout-game/brickout-game.py胜利brickWall.hasWin()为真12 块砖全部清空时gameStatus False画面显示 You win! 文本与分数brickout-game/brickout-game.py。游戏结束后窗口不会自动关闭玩家需要点击窗口的关闭按钮退出对应主循环中的QUIT事件处理。源码中值得注意的实现细节对二次开发友好结合 brickout-game/brickout-game.py 逐行阅读可以发现几个对二次开发有参考价值的细节README 的命令与仓库实际文件名不一致README 写的是python game.py而仓库实际文件为brickout-game.py运行时请以实际文件名为准。类中普遍存在传了screen参数但绘制时使用全局screen的现象四个类的构造方法都接收screen并存为self.__screen但draw方法内部直接引用了模块级全局变量screen如 brickout-game/brickout-game.py。从代码结构看这属于参数保留但未实际使用的冗余设计改动绘制逻辑时需注意这一点。疑似笔误底部掉出判定用了宽度而非高度Ball.update中球掉出底部的判定写的是self._yLoc self.__width - self._radiusbrickout-game/brickout-game.py而__width是屏幕宽度 700屏幕高度其实是__height500。其后果是球从底部y 500消失后还要继续下落直到 y 675 才触发 Game Over。从源码结构看这很可能是一个笔误应为self.__height但整体不影响游戏最终判定结果只是失败提示出现得更晚。若要移植或扩展建议修正为高度判定。类继承的混合用法Brick继承pygame.sprite.Sprite、BrickWall继承pygame.sprite.Group但BrickWall实际上主要用自定义的_bricks列表管理砖块Group的精灵管理能力并未被充分使用属于半引入的继承用法。计分按帧叠加如前所述分数按球与砖块墙相交的每一帧累加而非按砖块消失计数这是理解计分行为的关键。基于现有架构的扩展思路现有代码已经给出了一条清晰的扩展路径围绕类图与源码结构可以自然地做如下增强仅作思路参考仓库为只读不涉及修改让砖块一砖一命将计分点从帧级碰撞检测迁移到BrickWall.update移除砖块的位置即可实现严格的击碎一砖 10为砖块加入生命值与多颜色Brick类已经具备独立的坐标与绘制能力只需为每个实例增加生命值字段、按生命值切换颜色即可实现多级砖块挡板改用键盘控制Paddle.update当前只读鼠标坐标可以仿照其结构改为监听KEYDOWN/KEYUP事件调整_xLoc修正底部掉出判定将Ball.update中的__width改为__height让 Game Over 在球真正落出屏幕时立即触发增加球速与关卡__xVel/__yVel目前是写死的常量改为实例变量后即可在每局胜利后提速形成关卡难度递进。总的来说brickout-game/brickout-game.py 用不到 400 行代码完整实现了打砖块游戏的核心闭环——对象建模、碰撞检测、计分、胜负判定与 60 FPS 主循环是理解 Pygame 游戏开发最小可行架构的优质范例。【免费下载链接】PythonMy Python Examples项目地址: https://gitcode.com/gh_mirrors/py/Python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表