- 示例工程
- 教程
【免费下载链接】materials
Bonus materials, exercises, and example projects for our Python tutorials
导读:本文以 asterioids-pygame-project/source_code_final/README.md 中描述的 Space Rocks 项目为主线,结合仓库内从第 1 步到第 10 步的完整渐进式源码,讲解如何用 Python 3 与 Pygame 2.0 实现一款可运行的小行星射击游戏。读完本文,你将掌握 Pygame 游戏循环、精灵渲染、碰撞检测、边界环绕、键盘输入与音效播放的完整实现方案,并能直接运行、修改与扩展这套源码。
图:Space Rocks 使用的 800×600 宇宙背景贴图(source_code_final/assets/sprites/space.png),作为游戏窗口的背景层,在每一帧绘制时被铺满整个屏幕。
项目概览:一个麻雀虽小五脏俱全的 Pygame 游戏
Space Rocks(太空岩石)是使用Python 3与Pygame编写的一款简单太空射击游戏:玩家操控一艘飞船在宇宙中飞行,用激光击碎随机出现的小行星,小行星被击中后会分裂成更小的个体,清空所有小行星即获胜,飞船被撞到则游戏失败。
仓库中该项目位于asterioids-pygame-project/目录下,结构非常清晰:
- README.md 说明了这是 Pygame 小行星游戏教程的配套代码,共包含10 个分步源码目录(
source_code_step_1至source_code_step_10)加一个最终完整版(source_code_final),每个步骤对应教程中的一个开发阶段; - 每步目录都包含
space_rocks/包与requirements.txt依赖清单; - 随着步骤推进,
assets/目录逐步加入飞船、小行星、子弹精灵与激光音效。
最终版代码(source_code_final)采用四个 Python 模块构成一个规范的包,职责划分明确:
| 文件 | 职责 |
|---|---|
| space_rocks/main.py | 包入口,创建SpaceRocks实例并启动主循环 |
| space_rocks/game.py | 游戏主类:初始化、事件处理、游戏逻辑、绘制 |
| space_rocks/models.py | 游戏对象模型:GameObject、Spaceship、Asteroid、Bullet |
| space_rocks/utils.py | 工具函数:加载精灵/音效、边界环绕、随机位置与速度、文本渲染 |
环境准备与运行方式
根据项目文档,运行本游戏需要Python 3。依赖仅有一个 Pygame,且锁定在 2.0.0 版本(见 requirements.txt):
pygame==2.0.0安装与启动命令如下(在source_code_final目录下执行):
pip install -r requirements.txt python space_rockspython space_rocks之所以可以直接运行,是因为space_rocks/__main__.py提供了包级入口:它从game模块导入SpaceRocks类,在if __name__ == "__main__":保护下实例化并调用主循环:
from game import SpaceRocks if __name__ == "__main__": space_rocks = SpaceRocks() space_rocks.main_loop()运行后弹出的窗口标题为"Space Rocks",分辨率为800×600。操作方式与键位如下:
| 按键 | 功能 |
|---|---|
←/→ | 逆时针 / 顺时针旋转飞船 |
↑ | 沿当前方向加速推进 |
空格 | 发射激光 |
Esc或关闭窗口 | 退出游戏 |
游戏主循环:Pygame 应用的骨架
所有版本的game.py都遵循同一个经典 Pygame 主循环模式,这是理解整个项目的基础。以 最终版 game.py 为例:
def main_loop(self): while True: self._handle_input() self._process_game_logic() self._draw()每一帧按顺序执行三步:处理输入 → 更新游戏逻辑 → 绘制画面。其中_draw()在渲染完成后调用pygame.display.flip()将缓冲区内容呈现到屏幕,并用self.clock.tick(60)将帧率锁定在 60 FPS(见 game.py):
def _draw(self): self.screen.blit(self.background, (0, 0)) for game_object in self._get_game_objects(): game_object.draw(self.screen) if self.message: print_text(self.screen, self.message, self.font) pygame.display.flip() self.clock.tick(60)从源码结构可以看到,SpaceRocks.__init__完成了以下初始化工作(game.py):
- 调用
pygame.init()并设置窗口标题; - 通过
pygame.display.set_mode((800, 600))创建 800×600 的游戏窗口; - 用
load_sprite("space", False)加载宇宙背景(第二个参数False表示不需要 alpha 通道,纯色背景直接convert()即可提升绘制性能); - 创建
pygame.time.Clock()用于控制帧率; - 创建字号为 64 的默认字体
pygame.font.Font(None, 64),用于显示胜负消息; - 初始化小行星列表
self.asteroids、子弹列表self.bullets,并在屏幕中央(400, 300)生成飞船。
资源加载与工具函数:utils.py 的四个核心能力
utils.py 封装了游戏所需的底层工具,共四个函数:
1. 精灵加载load_sprite(name, with_alpha=True):按assets/sprites/{name}.png路径加载图片;with_alpha为True时调用convert_alpha()保留透明通道(适用于飞船、小行星、子弹),为False时调用convert()去掉透明通道(适用于整幅背景)。
2. 音效加载load_sound(name):按assets/sounds/{name}.wav路径加载并返回pygame.mixer.Sound对象。
3. 边界环绕wrap_position(position, surface):这是"小行星飞出屏幕又从另一侧回来"的关键实现:
def wrap_position(position, surface): x, y = position w, h = surface.get_size() return Vector2(x % w, y % h)对坐标分别对窗口宽高取模,物体移出屏幕一侧后自然从对侧重新出现。
4. 随机生成get_random_position与get_random_velocity(min_speed, max_speed):前者返回窗口范围内的随机坐标;后者生成速度大小在[min_speed, max_speed]区间、方向随机(0°~359°)的速度向量——实现方式是先构造一个指向 0° 的速度向量Vector2(speed, 0),再旋转随机角度:
def get_random_velocity(min_speed, max_speed): speed = random.randint(min_speed, max_speed) angle = random.randrange(0, 360) return Vector2(speed, 0).rotate(angle)此外,utils.py 中的print_text(surface, text, font, color=Color("tomato"))负责把游戏结束消息渲染到屏幕正中央,默认使用番茄红色。
游戏对象模型:models.py 的继承体系与向量物理
models.py 是整个游戏的核心抽象层,定义了四个类,并利用pygame.math.Vector2完成全部运动计算。模块顶部定义了向上的基准向量:
UP = Vector2(0, -1)GameObject:一切游戏对象的基类
GameObject是所有对象的公共基类(models.py):
- position / velocity:使用
Vector2存储位置与速度,便于向量运算; - radius:碰撞半径,取精灵宽度的一半
sprite.get_width() / 2; - draw():把精灵绘制到
position - Vector2(radius)处,即让精灵中心对齐对象位置; - move():
self.position = wrap_position(self.position + self.velocity, surface),即按速度更新位置并做边界环绕; - collides_with(other_obj):基于圆心距离的圆形碰撞检测,两对象中心距离小于半径之和即判定碰撞:
def collides_with(self, other_obj): distance = self.position.distance_to(other_obj.position) return distance < self.radius + other_obj.radiusSpaceship:可旋转、可加速、可开火的飞船
Spaceship在基类之上增加了三个可调参数(models.py):
| 类常量 | 数值 | 含义 |
|---|---|---|
MANEUVERABILITY | 3 | 每次按键旋转的角度(度) |
ACCELERATION | 0.25 | 每帧推进速度增量 |
BULLET_SPEED | 3 | 子弹相对飞船的前向速度 |
关键实现要点:
- 方向向量:
self.direction = Vector2(UP)初始指向上方,注意这里拷贝了一份UP,避免后续rotate_ip原地旋转污染全局UP; - rotate():
self.direction.rotate_ip(angle)原地旋转方向向量,顺时针为正角度; - accelerate():
self.velocity += self.direction * self.ACCELERATION,把方向向量乘以加速度累加到速度上,实现"推力"效果(速度会惯性累积,符合太空飞行手感); - draw():根据
self.direction.angle_to(UP)计算当前朝向与正上方的夹角,再用rotozoom(self.sprite, angle, 1.0)旋转精灵并绘制,1.0表示缩放倍率保持原尺寸; - shoot():子弹速度 = 飞船朝向 × 子弹速度常量 + 飞船当前速度(继承飞船惯性),然后通过构造函数传入的回调
create_bullet_callback把子弹加入游戏,同时播放激光音效self.laser_sound.play()(models.py)。
Asteroid:可分裂的小行星
Asteroid实现了经典的小行星分裂机制(models.py):
- 构造函数接收
size参数(默认 3),通过size_to_scale = {3: 1.0, 2: 0.5, 1: 0.25}映射缩放比例,用rotozoom(load_sprite("asteroid"), 0, scale)按比例缩放精灵——3 级小行星原尺寸,2 级半尺寸,1 级四分之一尺寸; - 速度由
get_random_velocity(1, 3)随机生成,即每次约 1~3 像素/帧、方向随机; - split():当被子弹击中且
size > 1时,在原位置分裂出两个size - 1的小行星,并通过回调create_asteroid_callback加入小行星列表。
Bullet:一去不回头的子弹
Bullet是最简单的对象(models.py),仅比基类多覆盖了move():子弹不做边界环绕,而是直线飞行:
def move(self, surface): self.position = self.position + self.velocity飞出屏幕的子弹会在游戏逻辑中被清理(见下文)。
输入处理:事件驱动与按键扫描的配合
game.py 的_handle_input()同时使用了 Pygame 的两种输入方式:
1. 事件队列(event queue)——处理离散事件:pygame.QUIT(关闭窗口)与K_ESCAPE触发quit()退出;按下K_SPACE且飞船仍存活时调用self.spaceship.shoot()。
2. 按键状态扫描(key.get_pressed())——处理持续按住的状态:按住K_RIGHT顺时针旋转、K_LEFT逆时针旋转、K_UP持续加速。
注意代码中所有涉及飞船的操作都先判断if self.spaceship:,因为飞船被小行星撞毁后会被置为None,后续帧不能再访问其属性,否则会引发AttributeError。
游戏逻辑:碰撞、分裂与胜负判定
game.py 的_process_game_logic()依次处理以下逻辑:
1. 移动所有对象:遍历_get_game_objects()(小行星 + 子弹 + 飞船)调用各自的move()。
2. 飞船与小行星碰撞 → 游戏失败:若飞船撞上任意小行星,将self.spaceship置为None并设置self.message = "You lost!"。
3. 子弹与小行星碰撞 → 分裂:遍历子弹与陨石的副本列表(self.bullets[:]与self.asteroids[:],拷贝副本避免遍历中修改原列表),命中后从原列表移除两者并调用asteroid.split()生成更小的碎片。
4. 清理飞出屏幕的子弹:if not self.screen.get_rect().collidepoint(bullet.position)判断子弹是否仍在窗口矩形内,不在则移除。
5. 胜利判定:当小行星列表清空且飞船仍存活时,设置self.message = "You won!"。
胜负消息最终由print_text渲染在屏幕中央。
十步渐进式构建路线:从空窗口到完整游戏
仓库以source_code_step_1至source_code_step_10十个目录呈现了游戏逐步成型的过程,是学习"自顶向下分步开发"的绝佳教材。根据对各目录源码的核对,每步引入的能力如下:
| 步骤 | 源码目录 | 新增能力 |
|---|---|---|
| Step 1 | source_code_step_1 | 创建 800×600 窗口与主循环骨架,蓝色填充背景,事件处理留空 |
| Step 2 | source_code_step_2 | 加入退出事件处理(QUIT/Esc) |
| Step 3 | source_code_step_3 | 引入utils.load_sprite,加载星空背景贴图 |
| Step 4 | source_code_step_4 | 建立models.py的GameObject基类,加入飞船与小行星精灵,引入Clock.tick(60) |
| Step 5 | source_code_step_5 | 实现Spaceship的旋转与加速,支持方向键操控 |
| Step 6 | source_code_step_6 | 实现Asteroid类与随机生成 6 颗小行星,引入边界环绕与飞船碰撞失败 |
| Step 7 | source_code_step_7 | 实现Bullet类与shoot(),支持空格开火 |
| Step 8 | source_code_step_8 | 加入子弹-小行星碰撞、小行星split()分裂机制 |
| Step 9 | source_code_step_9 | 加入激光音效laser.wav |
| Step 10 | source_code_step_10 | 加入字体渲染与 "You won!" / "You lost!" 胜负消息 |
例如,Step 1 的最小窗口版本只有二十余行(step_1 game.py):
class SpaceRocks: def __init__(self): self._init_pygame() self.screen = pygame.display.set_mode((800, 600)) def main_loop(self): while True: self._handle_input() self._process_game_logic() self._draw() def _handle_input(self): pass ...而 Step 10 的 最终 game.py 已具备完整的碰撞、分裂与胜负判定,与最终版几乎一致。这种渐进式结构让学习者可以在每个阶段运行代码、观察新增行为,是理解复杂游戏逐步叠加逻辑的最佳方式。
面向对象的架构设计亮点
从源码中可以提炼出几个值得学习的面向对象设计模式:
继承 + 多态:所有游戏对象继承
GameObject,统一了move()/draw()/collides_with()接口,使 game.py 的_get_game_objects()能用一份循环处理所有对象——列表拼接[*self.asteroids, *self.bullets]后再追加飞船,配合鸭子类型无需isinstance判断即可统一调用。回调注入(Callback Injection):
Asteroid与Spaceship的构造函数接收create_asteroid_callback/create_bullet_callback回调,把"创建对象"的职责交给拥有列表的SpaceRocks(self.asteroids.append、self.bullets.append)。这解耦了模型与游戏管理器:模型只负责产生新对象的事件,而由管理者决定如何维护对象集合,符合依赖倒置思想。向量物理:全程使用
pygame.math.Vector2完成位置、速度、方向与旋转运算(rotate_ip、angle_to、distance_to),避免了手写三角函数,代码简洁且精确。
运行与扩展建议
- 直接游玩:按上文命令安装依赖并
python space_rocks启动,用方向键移动、空格射击; - 调参:修改 models.py 中的
MANEUVERABILITY、ACCELERATION、BULLET_SPEED可调整手感;修改 game.py 中range(6)可改变初始小行星数量;MIN_ASTEROID_DISTANCE = 250(game.py)控制小行星生成时与飞船的最小安全距离; - 学习路线:对照十步目录按序运行,观察每个功能逐步接入的过程;若需对比差异,可将相邻步骤的
space_rocks/目录做 diff; - 潜在扩展方向(基于现有架构):计分系统(利用现有碰撞事件)、多条命(替换
spaceship = None逻辑)、更多精灵与音效(按assets/目录约定添加)、关卡难度递增(调整初始小行星数量与get_random_velocity速度区间)。
本仓库相关代码与资源均为开源教程的配套材料,可自由查看、运行与学习。
- 示例工程
- 教程
【免费下载链接】materials
Bonus materials, exercises, and example projects for our Python tutorials
相关推荐
python-mini-projects 太空射击小游戏:基于 Pygame 的 Space Bullet Shooter 实现与源码解析
python mini projects 太空射击小游戏:基于 Pygame 的 Space Bullet Shooter 实现与源码解析 本文以 python
示例工程使用 Python 与 Pygame 从零构建太空岩石(Asteroids)游戏:十步渐进实战全解析
使用 Python 与 Pygame 从零构建太空岩石(Asteroids)游戏:十步渐进实战全解析 本仓库中的 asterioids pygame proje
示例工程教程Web-Dev-For-Beginners 项目实战:用 JavaScript 从零构建太空射击游戏(Space Game)
Web Dev For Beginners 项目实战:用 JavaScript 从零构建太空射击游戏(Space Game) 导读 :本篇技术指南围绕 Web
文档教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考