☰
用 Python 与 Pygame 从零构建 Space Rocks 小行星游戏:源码解析与实战指南
2026/10/15 7:07:50 网站建设 项目流程
  • 示例工程
  • 教程

【免费下载链接】materials

Bonus materials, exercises, and example projects for our Python tutorials

项目地址:https://gitcode.com/gh_mirrors/ma/materials
点击查看免费下载

导读:本文以 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_rocks

python 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.radius

Spaceship:可旋转、可加速、可开火的飞船

Spaceship在基类之上增加了三个可调参数(models.py):

类常量数值含义
MANEUVERABILITY3每次按键旋转的角度(度)
ACCELERATION0.25每帧推进速度增量
BULLET_SPEED3子弹相对飞船的前向速度

关键实现要点:

  • 方向向量: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 1source_code_step_1创建 800×600 窗口与主循环骨架,蓝色填充背景,事件处理留空
Step 2source_code_step_2加入退出事件处理(QUIT/Esc)
Step 3source_code_step_3引入utils.load_sprite,加载星空背景贴图
Step 4source_code_step_4建立models.py的GameObject基类,加入飞船与小行星精灵,引入Clock.tick(60)
Step 5source_code_step_5实现Spaceship的旋转与加速,支持方向键操控
Step 6source_code_step_6实现Asteroid类与随机生成 6 颗小行星,引入边界环绕与飞船碰撞失败
Step 7source_code_step_7实现Bullet类与shoot(),支持空格开火
Step 8source_code_step_8加入子弹-小行星碰撞、小行星split()分裂机制
Step 9source_code_step_9加入激光音效laser.wav
Step 10source_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 已具备完整的碰撞、分裂与胜负判定,与最终版几乎一致。这种渐进式结构让学习者可以在每个阶段运行代码、观察新增行为,是理解复杂游戏逐步叠加逻辑的最佳方式。

面向对象的架构设计亮点

从源码中可以提炼出几个值得学习的面向对象设计模式:

  1. 继承 + 多态:所有游戏对象继承GameObject,统一了move()/draw()/collides_with()接口,使 game.py 的_get_game_objects()能用一份循环处理所有对象——列表拼接[*self.asteroids, *self.bullets]后再追加飞船,配合鸭子类型无需isinstance判断即可统一调用。

  2. 回调注入(Callback Injection):Asteroid与Spaceship的构造函数接收create_asteroid_callback/create_bullet_callback回调,把"创建对象"的职责交给拥有列表的SpaceRocks(self.asteroids.append、self.bullets.append)。这解耦了模型与游戏管理器:模型只负责产生新对象的事件,而由管理者决定如何维护对象集合,符合依赖倒置思想。

  3. 向量物理:全程使用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

项目地址:https://gitcode.com/gh_mirrors/ma/materials
点击查看免费下载
上一篇:3个维度解析Manim数学动画引擎:如何让抽象数学概念变得可视化?
下一篇:SpriteJS 事件系统实战指南:DOM 事件模型、命中检测与事件冒泡

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询