news 2026/10/22 7:51:28

Python实战:用Pygame开发外星人入侵游戏全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实战:用Pygame开发外星人入侵游戏全流程解析

简介:这是一份面向Python初学者的实战码源,以“外星人入侵”小游戏为主线,串联pygame环境搭建、主循环设计、精灵类创建、碰撞检测以及音效与图像加载等知识点,适合零基础者把第一个游戏项目完整跑起来,从基础语法到游戏循环逐步推进。zip压缩包仅182KB,代码精简、注释充分,可逐行理解从窗口创建、事件响应到游戏状态更新的完整流程。该资源已有660人学习下载,特别适合想通过游戏开发巩固面向对象编程、并独立扩展游戏功能的读者。借助这套码源,既能掌握pygame常用API的调用方式,也能学会用类组织飞船、外星人和子弹等对象;还可以通过事件队列处理按键,利用定时器控制射击节奏,理解碰撞发生后扣血、计分、重生等规则的实现。注释中对移动、渲染与计分逻辑的拆解,可作为后续2D游戏项目直接复用的骨架。

1. Python实践项目“外星人入侵”:把 pygames 从安装到通关一次走完

把“外星人入侵”这份 Python 码源完整跑通之后,你基本就摸清了 pygames 里 80% 的日常操作。这个项目不是一段演示片段,而是一个能玩、能改、能拆的完整 2D 射击游戏:玩家操控飞船左右移动,发射子弹拦截不断下落的外星人,击中加分、被撞掉命、全部清空后进入下一波。它最难得的地方在于,代码量不大,却把 Python 入门里最难啃的三块硬骨头——事件驱动的主循环、面向对象的游戏实体建模、碰撞检测与状态管理——全部串在一条游戏主线上。适合刚学完语法但不知道怎么写完整项目的初学者,也适合想快速捡起 pygames 的老手拿来当脚手架。你会发现,游戏开发里的“黑匣子”拆开之后,其实就是几段循环加几个类的事。

2. 跑通主循环:环境搭建、事件队列与 60 帧游戏的心脏

2.1 环境搭建:Python 版本、pygame 安装与验证

拿到这份码源之后,第一步不是读代码,而是把环境跑通。项目基于 pygame,这个库对 Python 版本的兼容性一直比较稳定,常见做法是用 Python 3.8 到 3.11 之间的版本,配 pip 安装 pygame 2.x 系列。你机器上如果已经装了多个 Python 版本,建议在项目里单独开一个虚拟环境,避免把依赖装到全局环境里,后面换项目容易打架。

python -m venv alien_env source alien_env/bin/activate # Windows 下用 alien_env\Scripts\activate pip install pygame python -c "import pygame; print(pygame.__version__)"

逻辑说明:第一条命令创建独立虚拟环境,第二条进入环境,第三条安装 pygame,第四条验证导入是否成功。如果最后一行能打印出版本号,比如 2.5.2,说明环境已经就绪。

参数说明:venv后面跟的alien_env是环境名,可以随便改;pip install pygame默认装最新版,如果遇到依赖冲突可以指定版本,比如pip install pygame==2.5.2。这里有个小细节:Windows 上如果提示No module named pygame,先确认你激活的是虚拟环境而不是系统 Python,再用where python看路径,这是初学者最容易翻车的地方。

2.2 主循环拆解:初始化、事件获取、状态更新、渲染

pygame 程序的核心是主循环,这份码源的alien_invasion.py就是围绕着它组织的。理解了这个循环,整份代码的骨架就抓住了。先看最基本的启动骨架:

import pygame pygame.init() screen = pygame.display.set_mode((1200, 800)) clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() screen.fill((230, 230, 230)) pygame.display.flip() clock.tick(60)

逻辑说明:pygame.init()一次性初始化所有模块,包括显示、事件、字体、音频等;set_mode创建游戏窗口并返回 Surface 对象;while True是主循环本体,每一帧都做三件事:取事件、画背景、刷新窗口。clock.tick(60)把帧率锁在 60 FPS,防止游戏在不同性能的电脑上跑出不同速度。

参数说明:(1200, 800)是窗口宽高,码源里用的就是这个尺寸,你也可以改成(800, 600)体验小窗口模式,但外星人的生成边界和飞船初始位置需要同步调整。screen.fill里的元组是 RGB 颜色,(230, 230, 230)是浅灰色背景。pygame.event.get()返回当前帧所有事件列表,这里只处理了 QUIT,也就是点窗口关闭按钮。

2.3 事件驱动机制:用 KEYDOWN 和 KEYUP 控制飞船移动

玩过游戏的人都知道,按住方向键飞船应该持续移动,松开就停。很多初学写法是只在 KEYDOWN 事件里写移动,结果按键一下只动一格,非常难受。正确做法是把“按键是否被按住”存成状态位,主循环里根据状态位持续刷新飞船位置。码源里用的就是这套逻辑:

while True: for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT: ship.moving_right = True elif event.key == pygame.K_LEFT: ship.moving_left = True elif event.type == pygame.KEYUP: if event.key == pygame.K_RIGHT: ship.moving_right = False elif event.key == pygame.K_LEFT: ship.moving_left = False if ship.moving_right: ship.rect.x += 1 if ship.moving_left: ship.rect.x -= 1

逻辑说明:KEYDOWN和KEYUP成对处理,按下时把对应标志置 True,松开置 False。主循环末尾再根据这两个标志决定是否移动,这样按住键时飞船会每帧都挪动,直到松开为止。这是 pygame 处理连续移动的标准模式,码源里到处都在用。

参数说明:代码里ship.moving_right是 Ship 类上的布尔属性,后面第三章会讲类怎么定义。ship.rect.x += 1的1是每帧移动的像素数,60 FPS 下就是每秒 60 像素,速度偏慢,你可以调成2或3,手感会明显不同。这里不用写event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT这种合并条件,因为 KEYUP 分支还要区分左右。

2.4 计时器和延迟控制:外星人什么时候该动了

游戏节奏靠两样东西控制:帧率锁和定时事件。帧率锁刚才已经用clock.tick(60)实现了,定时事件用pygame.time.set_timer()做。码源里生成外星人和限制子弹射速都用到了这套机制。

pygame.time.set_timer(pygame.USEREVENT + 1, 2000) # 主循环里: if event.type == pygame.USEREVENT + 1: create_alien()

逻辑说明:set_timer让事件队列每 2000 毫秒自动注入一个自定义事件,主循环里识别到这个事件就生成一个新外星人。这样你不用手动维护计数器,事件队列帮你把定时逻辑变成了事件处理逻辑,和按键、点击的处理方式完全统一。

参数说明:USEREVENT + 1是自定义事件编号,因为 pygame 预留的事件类型编号到USEREVENT为止,再往下要自己偏移。2000是间隔毫秒数,想加快节奏就调小到1000,想前期轻松一点就调大到3000。注意:set_timer只要设置一次就持续生效,不需要每帧调用。

3. 游戏对象用类来组织:飞船、外星人与子弹的设计与实例化

3.1 为什么游戏对象必须用类而不是字典

初学者看到类就头大,但游戏里对象太多了:一艘飞船、几十颗子弹、一队外星人。如果用字典存每个对象的 x、y、width、height,每个对象还得额外存一个类型字段,主循环里判断类型再写不同的更新逻辑,代码很快就变成一锅粥。类的价值在于把“对象有什么属性”和“对象能干什么”绑在一起。码源里每个游戏实体都是一个类,__init__负责初始化位置和尺寸,方法负责移动等行为,主循环只管调用方法,不用关心内部细节。

class Ship: def __init__(self, screen): self.screen = screen self.rect = pygame.Rect(0, 0, 50, 40) self.rect.midbottom = (600, 750) self.moving_right = False self.moving_left = False def update(self): if self.moving_right and self.rect.right < 1200: self.rect.x += 2 if self.moving_left and self.rect.left > 0: self.rect.x -= 2

逻辑说明:Ship类用一个pygame.Rect对象表示飞船的位置和碰撞体积,midbottom把飞船底部中心放到画布中间偏下。update方法每帧被主循环调用,根据moving_right和moving_left两个标志决定是否移动,right < 1200和left > 0两个边界判断防止飞船飞出屏幕。

参数说明:pygame.Rect(0, 0, 50, 40)的前两个参数会被midbottom覆盖,所以直接写 0 就行,后两个是宽高,决定飞船碰撞体积的大小。+= 2是每帧位移量,和第二章主循环里那行+= 1要保持一致。这里有一个很多教程不会讲的细节:Rect对象有x、y、left、right、top、bottom等一整套属性,改x之后left和right会自动同步,这就是 pygame 用 Rect 而不是裸坐标的原因。

3.2 用精灵组管理子弹:让代码从“单个对象”升级到“对象群”

飞船只有一个,但子弹最多时有十几颗,外星人可能有几十个。逐个写变量是不现实的,码源用的是pygame.sprite.Group——一个专门管理对象集合的容器。先把子弹类定义好,再用组统一管理和绘制。

class Bullet: def __init__(self, screen, ship): self.screen = screen self.rect = pygame.Rect(0, 0, 3, 15) self.rect.midtop = ship.rect.midtop self.color = (255, 0, 0) def update(self): self.rect.y -= 3 bullets = pygame.sprite.Group() new_bullet = Bullet(screen, ship) bullets.add(new_bullet)

逻辑说明:Bullet类初始化时把子弹底部中心对准飞船顶部中心,这样子弹从飞船正上方射出。update方法让子弹每帧向上移动 3 像素。Group负责收集所有子弹,下面的操作可以一次性完成:遍历更新、统一绘制、批量删除。

参数说明:rect = pygame.Rect(0, 0, 3, 15)里宽 3 高 15,是细长子弹的尺寸,可以按你喜欢调整,粗子弹有 8×20 的,但碰撞体验会不同。midtop = ship.rect.midtop的midtop表示上边中点。y -= 3的 3 是子弹速度,比飞船的 2 快,保证子弹能追上前方的外星人。

for bullet in bullets: bullet.update() bullets.draw(screen)

逻辑说明:第一行遍历组里所有子弹调用各自的 update,第二行一次性把所有子弹绘制到屏幕。这就是 Group 的优势:不用手写 for 循环去获取每个对象再调用方法,容器帮你把重复劳动压缩成了两行。

参数说明:bullets.draw(screen)需要每个子弹对象有image属性或rect属性才能正确绘制。这里用的是纯色矩形子弹,pygame 会直接用rect绘制色块。如果你后面想换图片子弹,给类加一个self.image = pygame.image.load(...)就够。

3.3 外星人类:批量生成与整体移动

外星人比子弹还多,而且行为模式更复杂:整队要先向右移动,碰到屏幕边缘就整体下移掉头。码源的做法是把单个外星人和整队的移动逻辑分开,单个外星人类只管自己的位置,整队移动用主循环里的循环处理。

class Alien: def __init__(self, screen, x, y): self.screen = screen self.rect = pygame.Rect(0, 0, 40, 30) self.rect.x = x self.rect.y = y # 主循环里生成一整行: for column in range(10): alien = Alien(screen, 50 + column * 100, 50) aliens.add(alien) # 整队移动: for alien in aliens: alien.rect.x += fleet_direction if alien.rect.right > 1200 or alien.rect.left < 0: fleet_direction *= -1 for alien in aliens: alien.rect.y += 10

逻辑说明:Alien的__init__接收 x、y 坐标,用循环生成一整行外星人,column * 100让每个外星人横向间隔 100 像素。整队移动部分用一个全局的fleet_direction变量控制方向,任何外星人碰到屏幕边缘,方向取反且全体下移 10 像素。

参数说明:range(10)生成 10 个外星人,50是起始横坐标,100是间距。想调整外星人密度就改这两个数。fleet_direction只有两个取值:1 和 -1,乘 -1 实现掉头。这里有个性能细节:判断边缘时遍历了整个组两次(第一次判断碰边,第二次下移),外星人数量在几十个时毫秒级完成,不用优化,但如果以后做到上百个敌人,可以考虑合并遍历。

4. 碰撞检测与得分反馈:子弹命中判定的三种做法和落地细节

4.1 碰撞检测的三种方案:从数学原理到 pygame 封装

碰撞检测是游戏逻辑里最核心的部分。常见做法有三种:第一种是每帧计算两个矩形是否相交,这是最直观但代码量最大的方案;第二种是判断两个物体中心的距离和半径之和的关系,适合圆形物体;第三种是使用pygame.Rect.colliderect或pygame.sprite.groupcollide这类封装好的方法。码源里用的是第三种,矩形相交判断由 pygame 内部完成,开发者只需要调用接口。这三种方案的选型逻辑很简单:物体形状规则且数量少,矩形碰撞足够;炮弹爆炸这类范围伤害用中心距法更贴近直觉;如果物体是不规则形状,才需要考虑像素级碰撞检测,但那个性能开销很大,这个游戏用不上。

collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

逻辑说明:这一行同时完成了“子弹是否击中外星人”的判断和“双方是否消失”的处理。groupcollide会遍历子弹组和外星人组的每一个组合,检测矩形重叠,返回一个字典,键是被击中的子弹,值是被击中的外星人列表。参数里的两个True分别表示“子弹碰到外星人后删除子弹”和“外星人被碰到后删除外星人”。

参数说明:如果你想做穿透弹,第一个参数改成False,子弹打中外星人不消失,但这个游戏的默认配置是两败俱伤。groupcollide的返回值字典非常有用,后面做计分系统时直接取字典长度。注意:这个方法只适用于pygame.sprite.Group里的对象,里面的子元素必须有rect属性,这也是前面定义类时必须写self.rect的原因。

4.2 子弹击中外星人的得分与清空逻辑

groupcollide返回的字典里藏着得分信息,每击中外星人一次就应该加一次分。但直接循环加分会遇到一个问题:主循环每帧都在跑,同一帧里击中了 3 个外星人,如果只按字典长度加分,可能只加了 3 分而实际击中了 3 个,这样反而对了。真正要注意的是击中外星人之后,下一波外星人什么时候生成。

collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) if collisions: for aliens_hit in collisions.values(): score += 10 * len(aliens_hit) if len(aliens) == 0: aliens.empty() bullets.empty() create_fleet()

逻辑说明:collisions是字典,collisions.values()是每个子弹击中的外星人列表,一个子弹最多打中一个外星人(因为子弹矩形很小),但用len(aliens_hit)取长度是为了兼容未来“范围爆炸伤害”的改造。击中外星人后如果外星人组空了,清空所有子弹并重新生成一整队外星人,这就是“通关进入下一波”的核心逻辑。

参数说明:10 * len(...)里的 10 是每个外星人的分值,你可以随意调整。len(aliens) == 0判断要求外星人组里确实删除了所有外星人,所以前面groupcollide的第二个参数必须传True,否则外星人永远删不干净,也就永远进不了下一波。create_fleet()这个函数在码源里是单独定义的,参照第三章的循环生成逻辑。

4.3 飞船被击中的反向判定与重置流程

子弹打外星人是双向碰撞中的正向,飞船撞外星人是反向。逻辑完全对称,只是处理结果不同:子弹打外星人加分,飞船撞外星人扣生命值并重置游戏状态。码源里的实现方式是比较飞船矩形和外星人矩形,如果重叠就执行“损失一条命”的流程。

if pygame.sprite.spritecollideany(ship, aliens): ships_left -= 1 if ships_left <= 0: game_active = False else: aliens.empty() bullets.empty() ship.center_ship() pygame.time.wait(500)

逻辑说明:spritecollideany(ship, aliens)检测飞船与外星人组中任意一个是否碰撞,返回被撞到的外星人对象或None。碰撞后先扣一条命,命扣完就把game_active置False进入游戏结束状态;命还有剩余就清空外星人和子弹、把飞船居中,再等 500 毫秒给玩家一个喘息时间。

参数说明:ships_left的初始值在码源里通常是 3,对应 3 条命。pygame.time.wait(500)里的 500 是毫秒数,等待期间游戏画面会完全静止,这个设计在后来版本里可以用“倒计时闪烁”替代,但初学者先学会用wait控制节奏更重要。注意:game_active这个标志位必须在主循环最开始就检查,如果为False就直接跳过所有游戏逻辑,只保留背景绘制,否则游戏结束后飞船还能移动,外星人还会继续下落。

4.4 得分显示:把数字画到屏幕上的两种方式

得分得让玩家看见才算数。码源里用 pygame 的字体模块把数字渲染成图片贴到屏幕。pygame 官方推荐用pygame.font.SysFont调系统字体,但跨平台时不同电脑字体不同,会出现文字大小不一的问题。常见做法是下载一个免费像素字体放进项目文件夹,码源里注释也提到了这一步可实现性。

font = pygame.font.SysFont(None, 48) score_image = font.render(f"Score: {score}", True, (255, 255, 255)) screen.blit(score_image, (10, 10))

逻辑说明:SysFont(None, 48)让 pygame 自动选择一个系统默认字体,高度 48 像素;render把字符串变成 Surface 图像,第二个参数True开启抗锯齿;blit把图像贴到屏幕左上角(10, 10)位置。这三行每一帧都要执行,因为分数变化时文本内容也要更新。

参数说明:font.render是文本渲染的核心方法,第三个参数是字体颜色,这里用白色在深色背景上显示。每帧重新 render 会有微小性能开销,但在分数这种低频变化场景里完全够用。如果你使用自定义字体,文件要放在项目根目录,并且加载后要确认路径写对,Windows 和 Linux 的路径写法不一样,这是码源注释里特别提醒过的。

5. 避坑指南:pygame 初学者的六个常见翻车现场

5.1 环境与启动类问题:导入失败和窗口闪退

现象:代码一运行就报ModuleNotFoundError: No module named 'pygame',或者窗口刚弹出来就闪退,控制台完全没输出。

原因:第一个报错绝大多数情况是 pip 装错了 Python 环境,尤其是电脑上装了多个 Python 版本时,pip install 装到了 Python 3.12,而运行脚本用的是 Python 3.8。窗口闪退的原因更隐蔽:你没有处理pygame.QUIT事件,或者主循环里引用了未定义的对象,异常被 pygame 的窗口吞掉了。

解决:先用python -m pip install pygame而不是pip install pygame,这样保证装到当前 Python 环境;闪退问题在脚本最开头加一个try...except先捕获异常看输出。另外强烈建议在编辑器里直接运行而不是双击 py 文件,这样能看到完整的 traceback。

python -m pip install pygame python alien_invasion.py > game_log.txt 2>&1

逻辑说明:python -m pip强制使用当前解释器的 pip,避免环境错乱;重定向输出把控制台信息写到日志文件里,细看报错定位问题。

参数说明:这个命令不是固定写法,只是排查用。真正跑游戏时不需要重定向,直接在终端看输出更直观。

5.2 移动与事件处理问题:按键粘滞和弹跳回溯

现象:按住右箭头松手后,飞船继续滑一小段才停;或者只按一下方向键,飞船移动了两步。

原因:按键粘滞十有八九是忘记处理 KEYUP 事件,飞船的moving_right标志一直停在True。移动两步的原因正好相反,你在 KEYDOWN 里写了ship.rect.x += 2,主循环每帧又检查moving_right再加一次,等于按一次键加了两次位移。

解决:坚持本章 2.3 里的状态位方案,KEYDOWN 和 KEYUP 只负责改标志,位置更新全部放update()。如果你发现飞船“跳”着走,再去检查主循环里是否同时写了两处位移逻辑——这是最常见的手误。

5.3 显示与刷新问题:背景永远是一片黑

现象:游戏窗口是黑色的,飞船、外星人都看不见,但程序能正常响应按键。

原因:要么是背景从来没画过,要么是画了但没刷新。新手最容易犯的是在主循环里忘了pygame.display.flip(),画面就停在第一帧的全黑状态。还有一个隐蔽原因:你在主循环外画了一次背景就以为完事了,但每一次flip()之后,上一帧的所有内容都会被清空,必须每帧重画背景和所有对象。

解决:主循环的固定模板是:先screen.fill()画背景,再blit所有对象,最后flip()刷新。顺序不能反,先画背景再画对象才能保证对象覆盖在背景上。检查一下你的代码里有没有flip(),没有的话补上,有的话确认它是不是在主循环内部而不是外部。

5.4 碰撞检测失效:子弹穿过外星人却不算命中

现象:子弹射向外星人,两者明显重叠,但groupcollide返回空字典,得分不涨。

原因:最常见的是外星人和子弹分属于不同的组,或者某个对象的rect属性不是真正的碰撞体积。比如你用图片加载外星人时,图片本身有透明边距,rect比可见区域大很多,碰撞点落在了不可见区域,感觉上“没碰到”但实际已经碰撞了。另外,如果子弹每帧移动距离大于外星人的宽度,子弹可能跳过外星人所在的格子——物理上叫“隧穿效应”。

解决:把子弹速度调小,比如从y -= 5改成y -= 2,减小隧穿概率。图片透明边距问题,用pygame.Surface.get_rect()拿到的矩形是图片全尺寸,你可以手动调整rect.width和rect.height缩小碰撞范围。还有一个调试技巧:在碰撞逻辑里临时加一行print(len(collisions)),如果控制台一直输出 0,基本就能确认是碰撞检测逻辑没执行到。

collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) if collisions: print(f"本帧命中 {len(collisions)} 个目标") # 临时调试

逻辑说明:加一行 print 输出能立刻告诉你groupcollide有没有生效。调试完记得删掉,不然游戏跑起来控制台刷屏。

参数说明:f"本帧命中..."是 f-string 写法,Python 3.6 以上都支持。如果连这行都不输出,问题出在bullets或aliens组本身就是空的,检查生成逻辑。

5.5 帧率与性能问题:外星人忽快忽慢地移动

现象:外星人移动速度不稳定,有时候一帧跑老远,有时候卡住几秒不动,和飞船的流畅移动完全不一样。

原因:这几乎可以肯定是没锁帧率。没有clock.tick(60)时,游戏帧率取决于电脑性能,强机器每秒跑 200 帧,外星人每帧移动 1 像素,看起来就是每秒 200 像素;弱机器每秒只跑 30 帧,外星人慢了一半。至于卡住几秒,通常是主循环里出现了耗时的操作,比如每帧重新加载图片文件,或者生成大量pygame.Rect对象没有及时释放。

解决:第一件事确认要求是clock.tick(60)已经写在主循环里。另外,把所有pygame.image.load()调用移到初始化阶段只做一次,把结果存成类的属性,不要在update()里反复加载。如果还是卡,用cProfile做一次性能分析,找出耗时最长的函数。

5.6 调试习惯:遇到诡异问题先做最小化复现

现象:游戏崩溃了,但报错信息指向一个完全不相干的行,排查半天找不到原因。

原因:这种“报错行和实际错误无关”的情况,多半是前面的代码里有某个状态没初始化就被使用了,比如外星人组还没生成就调用len(aliens),或者子弹碰巧在aliens组空的时候触发了碰撞检测分支。

解决:我的习惯是出了问题先减代码,把主循环里的功能一项项去掉,直到错误不再出现,再逐步加回来定位。这个项目只有几百行,最小化复现很快。用二分法也能加速:注释掉一半代码,看错误是否消失,然后缩小范围。血泪经验告诉我,pygame 的报错信息有时候会因为图形上下文的干扰变得不准确,只有精简到最小可运行脚本时,报错才会诚实。

6. 进阶改造:把计分、音效和关卡难度拧进同一套代码

项目跑通之后,真正的乐趣才开始。我通常会建议在这个阶段做三个顺手的小改造:音效反馈、关卡加速、计分板状态化。这三个改动都不大,但能让你从“会跑”变成“会改”。

先说音效。pygame 的音频模块初始化已经包含在pygame.init()里了,加载音乐和音效各用一套 API:

pygame.mixer.music.load("background.wav") pygame.mixer.music.play(-1) # -1 表示循环播放 shoot_sound = pygame.mixer.Sound("shoot.wav") shoot_sound.play()

逻辑说明:mixer.music负责背景音乐,Sound负责短音效。音乐加载后要放在主循环之前,音效则在触发动作时播放——发射子弹、击中外星人、飞船被撞,分别调用各自的play()。

参数说明:.wav文件格式兼容性最好;.mp3也能用但解码开销略大。音效文件建议控制在 1-2 秒以内,背景音乐可以循环播放。文件路径统一放sounds/文件夹,避免根目录太乱。

再说关卡加速。这个游戏最简单的加速逻辑是:每清空一波外星人,就把外星人移动速度和子弹射速都调快一点。码源里相关参数通常集中定义在设置区,改起来很方便。

if len(aliens) == 0: alien_speed *= 1.1 bullet_speed *= 1.05 create_fleet()

逻辑说明:清空一波后,速度变量乘以一个略大于 1 的系数,实现难度的平滑递增。1.1和1.05是增速系数,前者影响外星人,后者影响子弹,让玩家感觉世界越来越危险。

参数说明:增速系数不能设太大,1.5级的增速三波之后就会失控。建议先用1.1跑十波观察手感,再微调。这里的alien_speed和bullet_speed在码源里可能叫alien_speed_factor和bullet_speed_factor,命名不同但位置类似。

最后是计分板状态化。目前的计分是单纯的分数数字,稍作改造可以把它升级成全局状态对象,随时记录最高分、当前关卡、剩余生命,所有 UI 都从它取值。

class GameStats: def __init__(self): self.reset_stats() def reset_stats(self): self.ships_left = 3 self.score = 0 self.level = 1

逻辑说明:GameStats把分散的生命值、分数、关卡集中到一个对象里,reset_stats在每一局开始时重置状态。飞船被撞掉命、得分变化、关卡递增都修改这个对象的属性,主循环只读不直接改,逻辑清晰很多。

参数说明:reset_stats里三个字段的初始值对应游戏初始状态。如果你想做“最高分保留”,给类加一个self.high_score = 0,在reset_stats里不重置它就行。这一步改造之后,你已经摸到了 pygame 项目从零到一、从一到多的完整路径——从那以后我每次拿到一套新代码,都会强制走一遍“跑通主循环、拆开对象建模、定位碰撞边界、再做一次状态化重构”这四步,不是每一步都必要,但在所有项目里都能帮你快速摸清底细。希望帮到你。

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

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

Electron与CLI双入口架构:Homebrew和winget跨平台分发实战

1. 从“t3code”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“t3code”这个标题&#xff0c;加上关键词里那一串 Electron、CLI、Homebrew、winget&#xff0c;我脑子里第一反应是&#xff1a;这大概率是一个把本地开发工具链打包成桌面应用、同时提供命令行入口的…

作者头像 李华
网站建设 2026/10/16 18:04:19

Sentinel-1雷达影像参数全解析:从轨道到极化,避开形变监测的坑

1. 哨兵一号数据的基本盘&#xff1a;为什么雷达影像值得你花时间第一次接触哨兵一号&#xff08;Sentinel-1&#xff09;数据的人&#xff0c;十有八九是被它那套“黑话”劝退的。打开一个SAFE文件夹&#xff0c;里面一堆TIFF、XML、还有长得像乱码的文件名&#xff0c;跟平时…

作者头像 李华
网站建设 2026/10/14 19:05:59

360清理Pro深度评测:C盘空间清理与系统盘瘦身实战指南

1. 为什么C盘总是莫名其妙就红了很多人都有过这种体验&#xff1a;新买的电脑用了一年半载&#xff0c;C盘那个小饼图就从清爽的蓝色变成了刺眼的红色&#xff0c;系统开始弹窗提示"磁盘空间不足"&#xff0c;开机越来越慢&#xff0c;软件打开要转半天圈。你打开&qu…

作者头像 李华
网站建设 2026/10/14 20:26:48

高防IP防护上限详解:带宽、清洗能力与防御类型三重约束

做高防这一行的朋友应该都有同感&#xff1a;买高防 IP 最怕的不是贵&#xff0c;而是买了之后业务照挂&#xff0c;钱花了&#xff0c;骂没少挨。早几年我帮一个游戏客户接入高防&#xff0c;对方上来就问“最大能防多少 G”&#xff0c;销售也拍胸脯说“500G 随便打”。结果上…

作者头像 李华
网站建设 2026/10/15 21:48:38

别把上下文开满:3.0 Flash 256K 最稳背后的设置避坑

别把上下文开满&#xff1a;3.0 Flash 256K 最稳背后的设置避坑 【免费下载链接】Agnes-3.0-Flash 项目地址: https://ai.gitcode.com/hf_mirrors/Agnes-AI/Agnes-3.0-Flash Agnes 3.0 Flash 上线以来&#xff0c;社区最热的话题不是它 33B 参数、72 层的混合注意力架构…

作者头像 李华