news 2026/9/12 10:53:25

Python飞机大战项目实战:从pygame环境搭建到游戏开发与打包全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python飞机大战项目实战:从pygame环境搭建到游戏开发与打包全流程

简介:这是一份基于Python与Pygame实现的飞机大战小游戏完整项目源码,主要面向期末大作业、课程设计等场景,也可作为Pygame入门学习的参考项目。代码中包含详细注释,结构清晰,对新手友好,能帮助理解游戏循环、精灵类、碰撞检测、音效播放等核心概念。项目从玩家操控、敌机生成、子弹发射到碰撞爆炸、得分计分,模块划分明确,方便读者按需阅读和二次开发。资源包共60个文件,涵盖5个Python源码文件、39张游戏贴图与界面图片、12个WAV音效及1个背景音乐OGG文件,同时附带字体文件、文本说明和Markdown文档,整体仅1.65MB,部署简单,下载后即可运行体验。目前已有168人学习下载,适合需要快速完成高分作业或希望系统学习Pygame游戏开发的同学参考借鉴,项目功能完善、界面美观、操作简单,具有较高的实际应用与学习价值。

1. 为什么“飞机大战”总是 Python 期末大作业的高分标答

飞机大战是 Python 小游戏里的常青树,期末交这个题目的人不少,但高分真不多。原因在于这道题看起来简单,实际要完整跑通,必须在 1000 行左右的规模里把 Python 的类、模块、事件循环、碰撞检测、资源加载全部串一遍。老师打开一份名为python和pygame飞机大战小游戏项目源码(高分期末大作业).zip的作业时,第一眼不会去看你特效多炫,而是先看代码是不是一坨。真正的高分标答不是“能玩”,而是“能讲”:每个文件知道为什么存在,每个参数知道为什么是这个值。下面这套从环境、运行、拆模块到打包演示的完整写法,就是照着这个标准来的。

2. 先搭 Python + pygame 环境,让飞机大战窗口先跑起来

2.1 装 Python 和 pygame:先绕过 failed to build 'pygame' 的坑

如果你还没有可用的 Python 环境,先把官方安装包装好,安装时勾选Add Python to PATH,这是所有 python 安装教程里的第一步。装好后建议立即给项目建虚拟环境:python -m venv venv,Windows 激活命令是venv\Scripts\activate,macOS 和 Linux 是source venv/bin/activate。虚拟环境的价值在于答辩换电脑时,只要pip freeze > requirements.txt就能把依赖完整带走,不会出现“在我电脑上能跑”的尴尬。

然后安装 pygame。如果你的 Python 小版本比较新,很容易在命令行看到error: failed to build 'pygame' when getting requirements to build wheel。这个报错表示 pip 找不到匹配的 wheel,于是尝试从源码编译。老电脑缺编译器时,这个过程会卡很久,最终还报错。我一般用两条命令规避:

python -m pip install --upgrade pip wheel setuptools python -m pip install --only-binary=:all: pygame

第一条先把 pip 和构建工具刷新到最新,第二条里面的--only-binary=:all:告诉 pip:所有包都只允许使用二进制发行版,不要尝试源码构建。这样如果平台确实没有 pygame 的 wheel,pip 会立即给出“找不到匹配版本”的明确结论,而不是闷头编译到半夜。

如果强制二进制后没的装,直接装社区维护的 pygame-ce:

python -m pip install pygame-ce

pygame-ce 在代码里依然写import pygame,API 与官方几乎完全兼容,很多新 Python 版本的预编译包反而比官方出得更快。老师问起来,你可以说“用社区维护版解决 Python 版本兼容问题”,这本身就是一个加分项。

命令作用什么时候用
pip install pygame安装官方 pygamePython 版本较老、wheel 已发布时
pip install --only-binary=:all: pygame强制只用二进制,不编译源码想快速确认环境到底缺什么
pip install pygame-ce安装社区维护版官方 wheel 缺失或构建失败时

2.2 窗口、Surface 与事件循环:让飞机大战窗口不闪退

pygame 游戏最核心的其实是窗口循环。先把窗口跑起来,后面所有精灵、碰撞、计分才有地方放。这里是最小的可运行骨架:

import sys import pygame def main(): pygame.init() screen = pygame.display.set_mode((960, 640)) pygame.display.set_caption("飞机大战") clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() screen.fill((12, 20, 40)) pygame.display.flip() clock.tick(60) if __name__ == "__main__": main()

pygame.init()会初始化显示、音频、字体等子系统。如果项目里要加载图片、音效和中文字体,全量初始化最省心。set_mode的元组(960, 640)决定窗口大小,也决定了后面所有blit的坐标上限。fill先铺满背景,flip再把缓冲区一次性推到窗口,这个过程叫双缓冲,pygame 的 GUI 绘制都是先在内存 Surface 上完成,最后统一上屏。

clock.tick(60)让主循环每秒最多跑 60 次。去掉这一行,游戏会以几百帧运行,碰撞检测偶尔会出现“子弹穿过敌机”的跳跃现象,CPU 占用也会直接拉满。常见误用是把screen.fill写在循环外,结果画面全是拖影;或者拿到event之后不做pygame.quit()sys.exit(),导致关不掉窗口。只要保持“取事件、更新逻辑、绘制、翻页、延时”在同一循环里,窗口就不会闪退。

2.3 把资源目录约定好,避免演示时换路径就崩

很多大作业源码里到处都是"images/player.png"这类相对路径,在自己电脑上双击没问题,拷到教室电脑上从其他目录启动就崩。相对路径的参考点是“当前工作目录”,不是你源码所在目录。最稳的做法是单独建一个paths.py

from pathlib import Path BASE_DIR = Path(__file__).resolve().parent RESOURCE_DIR = BASE_DIR / "resources" IMAGE_DIR = RESOURCE_DIR / "images" SOUND_DIR = RESOURCE_DIR / "sounds" FONT_DIR = RESOURCE_DIR / "font"

Path(__file__).resolve().parent根据源码文件位置推算目录,和从哪个目录启动无关。这样项目里就能统一写from paths import IMAGE_DIR,换机器、换路径都不会崩。资源目录不存在时,尽早报错也比加载图片时抛看不懂的 traceback 好:在paths.py最后加一句assert RESOURCE_DIR.exists(), "缺少 resources 目录"足够。

3. 让飞机大战真正“打”起来:精灵、碰撞与计分

3.1 用 Sprite 类把玩家、子弹、敌机编成三个组

pygame 的sprite.Sprite是专门为“可移动绘制对象”设计的基类。继承它之后,类里必须有两个属性:imagerectGroup.draw()全靠这两个属性把图片画到屏幕上,少一个都会报错。玩家类的核心写法如下:

import pygame class Player(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.image = pygame.image.load("resources/images/player.png").convert_alpha() self.rect = self.image.get_rect(center=(x, y)) self.speed = 6 def update(self, keys): if keys[pygame.K_LEFT]: self.rect.x -= self.speed if keys[pygame.K_RIGHT]: self.rect.x += self.speed self.rect.x = max(0, min(self.rect.x, 960 - self.rect.width))

convert_alpha()把 PNG 的透明通道转成 pygame 内存格式,这样飞机图片的透明背景不会变成黑块。rect是每帧移动的基础,修改rect.xrect.y就是移动图片。边界判断写成max(0, min(...))比写两个if的意图更明确:先取右边界最小值,再取左边界最大值,最终保证玩家不出屏幕。

玩家、子弹、敌机应该放在三个不同的Group里。Group.update(*args)会把参数分发给组内每个精灵的update方法,例如player_group.update(keys)传键盘状态,bullets_group.update()不传参。这样主循环里不需要手写for循环,代码更省。

3.2 碰撞检测:groupcollide 的参数和返回值

游戏“打得起来”的关键碰撞。最常用的写法是groupcollide

hits = pygame.sprite.groupcollide(enemies, bullets, True, True) for enemy in hits: score += 10 explosion_sound.play()

groupcollide检查第一组里的每个精灵是否与第二组精灵重叠,返回一个字典:键是“被撞中的第一组成员”,值是一个列表,包含所有撞到它的第二组成员。后面两个True分别对应dokill_adokill_b:第一个 True 让被撞敌机从组里删除,第二个 True 让子弹同时删除。一枪一个,符合飞机大战的基础规则。

参数默认值作用
dokill_aFalse碰撞后是否立即删除第一组精灵
dokill_bFalse碰撞后是否立即删除第二组精灵
collidedNoneNone表示矩形碰撞;传pygame.sprite.collide_mask做像素级碰撞

矩形碰撞的问题是:PNG 图片透明边缘依然算在rect里,经常出现“子弹离敌机还有 5 像素就判定命中”的违和感。想要精确,把参数改成pygame.sprite.groupcollide(enemies, bullets, True, True, pygame.sprite.collide_mask)。像素级碰撞根据图片非透明区域判断,观感真实,代价是 CPU 消耗变大。飞机大战同屏对象的数量一般不会超过几十个,这个开销可以忽略。

3.3 用时间戳控制敌机生成,别用 time.sleep

敌机生成的正确方式是基于毫秒时间戳做间隔判断:

now = pygame.time.get_ticks() if now - last_enemy_time >= ENEMY_SPAWN_INTERVAL: enemies.add(Enemy()) last_enemy_time = now

get_ticks()返回pygame.init()后经过的毫秒数,适合做冷却、倒计时和连发间隔。它不会暂停程序,不影响键盘响应。反过来,time.sleep(0.1)虽然也能生成间隔效果,但会阻塞整个主循环,轻则按键迟钝,重则音效断断续续。想加难度,可以让生成间隔随关卡动态变化,二.段写法:

enemy_interval = max(200, 800 - level * 80)

每过一关,间隔少 80 毫秒,但不会低于 200 毫秒,避免后期敌机堆成墙。

3.4 计分与中文字体:pygame 字体渲染的隐藏条件

计分界面要显示“得分”而不是“Score”,需要处理中文字体。pygame 的Font加载字体文件时,文件不存在不报错,等到render才抛异常;系统英文字体渲染中文会变成方块。正确的做法是准备一个中文 .ttf 字体文件:

font = pygame.font.Font("resources/font/msyh.ttf", 28) score_surface = font.render(f"得分: {score}", True, (255, 255, 255)) screen.blit(score_surface, (20, 20))

第二个参数True表示开启抗锯齿,文字边缘更平滑。font对象创建一次放循环外,不要每帧都重新加载字体文件——虽然一个字体文件加载几十次不至于崩溃,但答辩时老师如果盯着你的代码问性能优化,这就是第一个能答的点。

4. 面向期末大作业的飞机大战源码结构:拆成老师愿意翻的模块

4.1 一个模块只放一个对象:单文件的第一次重构

很多同学交上来的“项目源码”只有一个main.py,一千多行,老师翻到一半就失去耐心。高分大作业的源码,一定要让人能在 30 秒内找到入口和核心文件。常见的目录结构如下:

plane_war/ ├─ main.py ├─ settings.py ├─ paths.py ├─ sprites/ │ ├─ __init__.py │ ├─ player.py │ ├─ enemy.py │ └─ bullet.py ├─ ui.py └─ resources/ ├─ images/ ├─ sounds/ └─ font/

main.py只放启动、主循环和状态切换;sprites/目录里每个文件负责一种精灵;ui.py负责血条、得分和菜单绘制。这个结构本身就是答辩提纲。sprites/下必须有一个__init__.py,Python 3 里它让目录变成一个包,之后才能写from sprites.player import Player。资源目录不需要__init__.py,它不是被导入的模块。

4.2 settings.py 集中管理参数,避免“魔法数字”

同一份源码里如果9606800散落在多个文件,你会发现自己调一次手感要改十几个位置。把所有可变参数集中到一个settings.py里,是成本最低的架构优化:

# settings.py SCREEN_WIDTH = 960 SCREEN_HEIGHT = 640 FPS = 60 PLAYER_SPEED = 6 BULLET_SPEED = -12 ENEMY_SPAWN_INTERVAL = 800 ENEMY_SPEED = 3
参数建议值作用
SCREEN_WIDTH960窗口宽度,也是边界判断的右边界
SCREEN_HEIGHT640窗口高度
FPS60主循环目标帧率
PLAYER_SPEED6玩家每帧移动像素数
BULLET_SPEED-12子弹 Y 方向速度,负数表示向上
ENEMY_SPAWN_INTERVAL800敌机生成间隔毫秒
ENEMY_SPEED3敌机下落速度

在业务文件里用import settings,然后写settings.PLAYER_SPEED,比from settings import *更清晰。修改游戏难度只需要打开settings.py一个文件,老师拷到其他电脑上,也不用满项目找 960 是哪里的魔法数字。

4.3 用 state 状态机串起菜单、游戏中、结束

没有状态机的飞机大战只有一个“游戏进行中”分支,加个主菜单就得套一堆 flag。最轻量的状态玩法是用一个字符串变量:

state = "menu" while running: if state == "menu": draw_menu(screen) if start_key_pressed: state = "playing" elif state == "playing": update_game() draw_game(screen) if player.life <= 0: state = "game_over" elif state == "game_over": draw_game_over(screen) if retry_key_pressed: reset_game() state = "playing"

state让主循环按分支执行,每个分支的代码量都很小。以后想加暂停,加一个state == "pause"分支就行。期末大作业里最常见的“菜单 → 游戏 → 结束 → 重开”闭环,用这种方式写,老师追问时你能清楚说出当前状态和数据归属。

4.4 兼容源码运行和 exe 运行的资源路径封装

写完游戏后,你一定会打包成 exe 发给老师。打包后资源文件会被解压到临时目录,源码里写死的相对路径全部失效。提前封装一个resource_path函数能同时兼容两种运行方式:

import sys from pathlib import Path BASE_DIR = Path(__file__).resolve().parent def resource_path(relative_path: str) -> Path: base = getattr(sys, "_MEIPASS", BASE_DIR) return Path(base) / relative_path

源码运行时sys._MEIPASS不存在,getattr返回默认值BASE_DIR,资源路径和平时一样。PyInstaller 打包后,sys._MEIPASS指向临时解压目录,资源路径自动切换。两个模式用同一个函数,代码里不需要再写任何if判断。

5. 给飞机大战打包并录演示:答辩前最值得花时间的两个步骤

5.1 用 PyInstaller 打包,注意 --add-data 这个分号

源码跑通后,别急着把.py文件压进 zip。先用 PyInstaller 打包成单个 exe 试运行一遍。命令如下:

python -m PyInstaller --onefile --windowed --name plane_war --add-data "resources;resources" main.py

--onefile把所有内容合成一个 exe,运行时自动解压到临时目录;--windowed隐藏黑色控制台,否则玩家会看到一个命令行窗口;--name设置输出文件名;--add-data把资源目录映射进临时目录,前面是源码里的资源路径,后面是打包后目标目录名。Windows 上必须用分号分隔,macOS 和 Linux 用冒号,写错会打包成功但运行时找不到图片和字体。

PyInstaller 参数作用
--onefile打包成单个可执行文件
--windowed不显示控制台窗口
--name设置产物文件名
--add-data附带资源文件,格式是“源路径;目标路径”

打包后的 exe 一定要用resource_path里的逻辑加载资源。调试阶段可以临时去掉--windowed,让 print 输出显示在控制台里,方便确认resource_path返回的临时路径是否真实存在。

5.2 录两种演示片段,给老师留两个提问钩子

期末大作业的视频不需要长,1 分钟就够了。第一段录正常难度,从主菜单开始打一波敌机;第二段录参数调整对比,打开settings.py,把ENEMY_SPAWN_INTERVAL从 800 改成 300,保存后重新运行,老师能直观看到敌机密度变化。录屏时声音调低但不要静音,开火和爆炸音效能掩盖画面抖动上的小瑕疵;录屏帧率设 60,导出时用 30 帧,文件小且流畅。

如果老师让你现场演示,不要只打开游戏。先把项目目录和settings.py放在屏幕可见位置,再从命令行启动游戏,主动展示代码和进程的对应关系。如果老师问“这是你写的吗”,别急着辩解,直接改一行PLAYER_SPEED,保存重启让飞机速度明显变快,这个动作比说十句“是我自己写的”都有说服力。README 第一行放这条 PyInstaller 打包命令,老师按着做一遍能复现,遇到--add-data分号问题还能顺着你留下的注释排错。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 10:53:24

ComfyUI+MinMax-H3音画对齐视频生成实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 10:51:45

自动化测试框架在提示工程中的应用与优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 10:51:37

微信小程序实现4S店预约系统的关键技术解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 10:51:32

基于SSM框架的教材管理系统开发实践

1. 项目概述与背景教材管理系统是教育机构信息化建设中的核心组成部分&#xff0c;它直接关系到教学资源的有效管理和高效利用。基于JavaWeb和MySQL的SSMMaven技术栈实现的教材管理系统&#xff0c;采用了当前企业级Java开发的主流框架组合&#xff0c;为中小型教育机构提供了一…

作者头像 李华
网站建设 2026/9/12 10:49:40

冷热电气四联供系统优化与碳交易实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 10:48:35

Gitee研发一体化选型:从代码托管到CI/CD的完整闭环

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华