尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

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 venvWindows 激活命令是venv\Scripts\activatemacOS 和 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 的 wheelpip 会立即给出“找不到匹配版本”的明确结论而不是闷头编译到半夜。如果强制二进制后没的装直接装社区维护的 pygame-cepython -m pip install pygame-cepygame-ce 在代码里依然写import pygameAPI 与官方几乎完全兼容很多新 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.pyfrom 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 / fontPath(__file__).resolve().parent根据源码文件位置推算目录和从哪个目录启动无关。这样项目里就能统一写from paths import IMAGE_DIR换机器、换路径都不会崩。资源目录不存在时尽早报错也比加载图片时抛看不懂的 traceback 好在paths.py最后加一句assert RESOURCE_DIR.exists(), 缺少 resources 目录足够。3. 让飞机大战真正“打”起来精灵、碰撞与计分3.1 用 Sprite 类把玩家、子弹、敌机编成三个组pygame 的sprite.Sprite是专门为“可移动绘制对象”设计的基类。继承它之后类里必须有两个属性image和rect。Group.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.x和rect.y就是移动图片。边界判断写成max(0, min(...))比写两个if的意图更明确先取右边界最小值再取左边界最大值最终保证玩家不出屏幕。玩家、子弹、敌机应该放在三个不同的Group里。Group.update(*args)会把参数分发给组内每个精灵的update方法例如player_group.update(keys)传键盘状态bullets_group.update()不传参。这样主循环里不需要手写for循环代码更省。3.2 碰撞检测groupcollide 的参数和返回值游戏“打得起来”的关键碰撞。最常用的写法是groupcollidehits pygame.sprite.groupcollide(enemies, bullets, True, True) for enemy in hits: score 10 explosion_sound.play()groupcollide检查第一组里的每个精灵是否与第二组精灵重叠返回一个字典键是“被撞中的第一组成员”值是一个列表包含所有撞到它的第二组成员。后面两个True分别对应dokill_a和dokill_b第一个 True 让被撞敌机从组里删除第二个 True 让子弹同时删除。一枪一个符合飞机大战的基础规则。参数默认值作用dokill_aFalse碰撞后是否立即删除第一组精灵dokill_bFalse碰撞后是否立即删除第二组精灵collidedNone传None表示矩形碰撞传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 nowget_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__.pyPython 3 里它让目录变成一个包之后才能写from sprites.player import Player。资源目录不需要__init__.py它不是被导入的模块。4.2 settings.py 集中管理参数避免“魔法数字”同一份源码里如果960、6、800散落在多个文件你会发现自己调一次手感要改十几个位置。把所有可变参数集中到一个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 playingstate让主循环按分支执行每个分支的代码量都很小。以后想加暂停加一个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分号问题还能顺着你留下的注释排错。本文还有配套的精品资源点击获取
返回列表