拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python游戏开发入门:用Pygame实现《外星人入侵》项目

1. 从零到一为什么选择《外星人入侵》作为你的第一个Python游戏项目如果你刚开始学习Python或者已经掌握了基础语法但还没做过一个完整的项目那么《外星人入侵》绝对是一个绝佳的起点。这个项目听起来很酷像是要搞个大制作但实际上它的核心逻辑非常清晰用到的技术点也恰好是初学者从“写脚本”到“做项目”必须跨越的那道坎。很多人学Python变量、循环、函数都懂了但一说到“面向对象”、“事件驱动”、“游戏循环”这些词就发怵感觉离自己很远。《外星人入侵》这个项目就是把这些抽象概念塞进一个你亲手能操控飞船、射击外星人的有趣盒子里让你在“玩”的过程中自然而然地掌握它们。这个项目的魅力在于“麻雀虽小五脏俱全”。它不需要复杂的3D引擎用pygame这个经典的2D游戏库就足够了。你需要处理玩家飞船的移动和射击需要管理一群有规律移动的外星人需要检测子弹和外星人的碰撞还需要计分、记录等级和生命。整个过程你会频繁地和“类”打交道——把飞船、子弹、外星人都设计成独立的类这是面向对象编程最直观的实践。你会用到“函数”来封装重复的逻辑比如检查边缘、更新编组。最重要的是你会深刻理解“编组”的概念pygame.sprite.Group这个工具能让你优雅地管理屏幕上几十个甚至上百个活动对象没有它代码会乱成一团。至于“图像”那就是给这些逻辑骨架穿上皮肤让一切可视化。所以别被“游戏”二字吓到。我们做的不是一个商业级的游戏而是一个教学意义大于娱乐意义的编程练习。目标是让你通过构建它打通Python项目开发的任督二脉。接下来我会带你一步步拆解从环境搭建到最终发射子弹击落外星飞碟把每个环节的原理和“为什么这么做”讲透。2. 项目基石搭建开发环境与理解Pygame的核心循环工欲善其事必先利其器。我们首先得把舞台搭好。2.1 创建专属的虚拟环境与安装Pygame我强烈建议你为这个项目创建一个独立的虚拟环境。这能保证项目依赖的纯净避免和你电脑上其他Python项目的库版本冲突。打开你的终端Windows用CMD或PowerShellmacOS/Linux用Terminal跟着做# 1. 为项目创建一个新目录并进入它 mkdir alien_invasion cd alien_invasion # 2. 创建虚拟环境这里使用Python内置的venv模块 python -m venv venv # 3. 激活虚拟环境 # 在Windows上 venv\Scripts\activate # 在macOS/Linux上 source venv/bin/activate # 激活后你的命令行提示符前通常会显示 (venv)表示已进入该环境。 # 4. 安装pygame。使用pip安装指定一个较新的稳定版本。 pip install pygame2.5.2为什么是Pygame 2.5.2这是一个经过长期测试、文档丰富且稳定的版本。Pygame社区活跃新版本可能带来API微小变动对于学习项目选择一个确定的版本能避免不必要的兼容性问题。安装成功后可以写一个超简单的测试脚本test_pygame.py来验证import pygame import sys pygame.init() print(“Pygame 初始化成功版本”, pygame.version.ver) pygame.quit() sys.exit()运行它如果没报错并打印出版本号恭喜你环境搞定。2.2 解剖Pygame游戏的心脏事件循环与主循环这是理解任何Pygame项目的关键。一个游戏不是一次性执行完就结束的它必须持续运行每秒几十次地做四件事处理用户输入、更新游戏状态、重新绘制屏幕、控制帧率。这个持续运转的机制就是游戏主循环。我们来创建一个最基础的窗口并实现这个循环import sys import pygame class AlienInvasion: 管理游戏资源和行为的类 def __init__(self): 初始化游戏创建游戏资源 pygame.init() # 设置屏幕尺寸。我们用一个固定的窗口而不是全屏便于调试。 self.screen pygame.display.set_mode((1200, 800)) pygame.display.set_caption(“外星人入侵”) # 设置背景色 (R, G, B)。深灰色比纯黑柔和。 self.bg_color (230, 230, 230) def run_game(self): 开始游戏主循环 while True: # 1. 监视键盘和鼠标事件 for event in pygame.event.get(): if event.type pygame.QUIT: pygame.quit() sys.exit() # 2. 更新游戏状态目前还没有需要更新的对象 # 3. 用背景色填充屏幕擦除旧帧 self.screen.fill(self.bg_color) # 4. 让最近绘制的屏幕可见双缓冲切换 pygame.display.flip() if __name__ ‘__main__’: # 创建游戏实例并运行 ai_game AlienInvasion() ai_game.run_game()把这段代码保存为alien_invasion.py并运行。你会看到一个灰色的窗口点击关闭按钮可以退出。我们来拆解这个循环pygame.event.get(): 这是消息泵。它获取所有在上一帧到这一帧之间发生的事件比如按键、鼠标移动、点击关闭按钮。我们遍历这些事件并对特定类型做出响应。pygame.QUIT事件就是用户点击了窗口关闭按钮。更新游戏状态目前是空的。后续我们会在这里调用飞船、外星人、子弹的update()方法让它们根据输入和时间改变位置。self.screen.fill(): 用背景色清空整个屏幕。因为游戏是逐帧绘制的如果不擦除上一帧你会看到物体移动的拖影。pygame.display.flip(): 这是关键。Pygame使用双缓冲技术。我们在一个“后台”表面self.screen上绘制所有图像当一帧的所有绘制命令完成后调用flip()将后台表面切换到前台显示。这样能避免屏幕闪烁让动画更平滑。这个循环会以你电脑能跑的最快速度运行这会导致CPU占用率极高且游戏速度在不同性能电脑上不一致。因此我们通常需要时钟Clock来控制帧率。在__init__方法里添加self.clock pygame.time.Clock()。 在run_game循环的末尾添加self.clock.tick(60)。clock.tick(60)告诉Pygame“确保这个循环每秒最多运行60次。” 这就将游戏帧率限制在了60FPS既保证了流畅性又控制了资源消耗。这是专业游戏开发的标准做法。3. 构建游戏世界的第一公民玩家飞船类游戏有了舞台和循环现在需要主角——一艘可以由玩家控制的飞船。我们将用面向对象的思想来创建它。3.1 设计Ship类属性与初始化在项目根目录下创建一个新文件ship.py。我们将飞船定义为一个独立的类这样它的数据位置、图像和行为移动、绘制都封装在一起。import pygame class Ship: 管理飞船的类 def __init__(self, ai_game): 初始化飞船并设置其初始位置 # 获取游戏屏幕的surface对象和矩形区域方便后续定位 self.screen ai_game.screen self.screen_rect ai_game.screen.get_rect() self.settings ai_game.settings # 稍后我们会创建游戏设置类 # 加载飞船图像并获取其外接矩形 self.image pygame.image.load(‘images/ship.bmp’) self.rect self.image.get_rect() # 将每艘新飞船放在屏幕底部中央 self.rect.midbottom self.screen_rect.midbottom # 在飞船的属性中存储一个浮点数表示的精确水平位置 self.x float(self.rect.x) # 移动标志用布尔值记录按键状态比在事件循环中直接移动更平滑 self.moving_right False self.moving_left False def update(self): 根据移动标志调整飞船的位置 # 更新飞船的x值而不是直接修改rect.x if self.moving_right and self.rect.right self.screen_rect.right: self.x self.settings.ship_speed if self.moving_left and self.rect.left 0: self.x - self.settings.ship_speed # 根据self.x更新rect对象 self.rect.x int(self.x) def blitme(self): 在指定位置绘制飞船 self.screen.blit(self.image, self.rect)关键点解析为什么传ai_game参数飞船需要知道它在哪个屏幕上绘制需要访问游戏设置如速度。通过传入主游戏对象我们建立了两者间的关联这是一种常见的依赖注入方式比使用全局变量更清晰。self.rect与self.xrect是Pygame的矩形对象属性x,y,centerx,bottom等都是整数。直接修改rect.x进行移动没问题但如果你想做更精细的速度控制比如速度是1.5像素/帧整数会丢失精度。所以我们用浮点数self.x来存储精确位置在最终绘制前再转换为整数赋给rect.x。移动标志Flag这是实现连续移动的经典模式。在事件循环中我们只设置标志位按下键设为True松开键设为False。在update()方法中根据标志位来真正改变位置。这样做的好处是移动响应平滑且可以轻松处理多个按键同时按下的情况。边界检查self.rect.right self.screen_rect.right确保飞船不会移出屏幕右边界。左边界同理。blitme()blit是位块传输的意思即将一个图像表面绘制到另一个表面上。这个方法封装了绘制操作。你需要准备一张飞船图片命名为ship.bmp放在项目根目录下的images文件夹中。图片背景最好是透明的或者与游戏背景色一致格式BMP是Pygame最直接支持的当然你也可以用PNG但加载方式略有不同pygame.image.load也支持PNG。3.2 在主游戏中集成飞船并响应键盘事件现在回到alien_invasion.py我们需要做几件事创建一个设置类来管理配置、实例化飞船、在循环中更新和绘制飞船并处理键盘事件。首先创建settings.pyclass Settings: 存储游戏《外星人入侵》中所有设置的类 def __init__(self): 初始化游戏的静态设置 # 屏幕设置 self.screen_width 1200 self.screen_height 800 self.bg_color (230, 230, 230) # 飞船设置 self.ship_speed 5.0然后修改alien_invasion.pyimport sys import pygame from settings import Settings from ship import Ship class AlienInvasion: def __init__(self): pygame.init() self.settings Settings() # 创建设置实例 self.screen pygame.display.set_mode( (self.settings.screen_width, self.settings.screen_height)) pygame.display.set_caption(“外星人入侵”) self.clock pygame.time.Clock() # 创建一艘飞船。注意将self当前游戏实例传递进去 self.ship Ship(self) def run_game(self): while True: self._check_events() # 处理事件 self.ship.update() # 更新飞船位置 self._update_screen() # 绘制新屏幕 self.clock.tick(60) # 控制帧率 def _check_events(self): 响应按键和鼠标事件 for event in pygame.event.get(): if event.type pygame.QUIT: pygame.quit() sys.exit() elif event.type pygame.KEYDOWN: # 按键按下 self._check_keydown_events(event) elif event.type pygame.KEYUP: # 按键松开 self._check_keyup_events(event) def _check_keydown_events(self, event): 响应按下 if event.key pygame.K_RIGHT: self.ship.moving_right True elif event.key pygame.K_LEFT: self.ship.moving_left True elif event.key pygame.K_q: # 按Q键也可以退出游戏方便调试 pygame.quit() sys.exit() def _check_keyup_events(self, event): 响应松开 if event.key pygame.K_RIGHT: self.ship.moving_right False elif event.key pygame.K_LEFT: self.ship.moving_left False def _update_screen(self): 更新屏幕上的图像并切换到新屏幕 self.screen.fill(self.settings.bg_color) self.ship.blitme() # 绘制飞船 pygame.display.flip() if __name__ ‘__main__’: ai_game AlienInvasion() ai_game.run_game()现在运行游戏你应该可以用左右方向键控制飞船在屏幕底部水平移动了这里我们把事件处理抽成了独立的方法_check_events并进一步细分为按下和松开的事件处理让代码结构更清晰。开头的下划线_是一种约定暗示这些是类内部的“私有”方法虽然Python不强制但这是一个好的编码习惯。4. 发射子弹理解精灵编组与对象管理飞船能动了接下来要赋予它攻击能力。子弹是游戏中典型的“批量产生、批量销毁、批量更新”的对象这正是Pygame中Sprite精灵和Group编组大显身手的地方。4.1 创建Bullet类并继承Sprite在bullet.py中我们让Bullet类继承pygame.sprite.Sprite。继承后我们的子弹对象就能享受到编组管理的便利。import pygame from pygame.sprite import Sprite class Bullet(Sprite): 管理飞船发射的子弹的类 def __init__(self, ai_game): 在飞船当前位置创建一个子弹对象 super().__init__() # 初始化父类Sprite self.screen ai_game.screen self.settings ai_game.settings self.color self.settings.bullet_color # 在(0,0)处创建一个表示子弹的矩形再设置正确的位置。 # 我们不用图像而是用绘制矩形来代表子弹更简单。 self.rect pygame.Rect(0, 0, self.settings.bullet_width, self.settings.bullet_height) # 将子弹的初始位置设置为飞船的顶部中央 self.rect.midtop ai_game.ship.rect.midtop # 用浮点数存储子弹的精确垂直位置 self.y float(self.rect.y) def update(self): 向上移动子弹 # 更新表示子弹位置的浮点数 self.y - self.settings.bullet_speed # 更新表示子弹位置的rect对象 self.rect.y int(self.y) def draw_bullet(self): 在屏幕上绘制子弹 pygame.draw.rect(self.screen, self.color, self.rect)同时在settings.py的__init__方法中添加子弹设置# 子弹设置 self.bullet_speed 10.0 self.bullet_width 3 self.bullet_height 15 self.bullet_color (255, 60, 60) # 亮红色关键点解析继承Sprite这允许我们将子弹对象添加到pygame.sprite.Group()中。编组可以自动调用组内所有精灵的update()方法并提供了强大的碰撞检测功能。使用Rect而非图像对于子弹这种简单的几何形状直接用pygame.Rect创建并绘制比加载图片文件更高效、更简单。self.y浮点数和飞船一样为了精确控制速度。update()方法编组会为每个精灵调用此方法。这里让子弹的y坐标不断减小实现向上飞行的效果。4.2 在游戏中管理子弹编组现在需要在主游戏AlienInvasion类中创建子弹编组并修改事件处理和主循环来支持发射。首先在alien_invasion.py顶部导入Bullet类from bullet import Bullet。然后在AlienInvasion.__init__中初始化一个用于存放子弹的编组# 创建一个用于存储子弹的编组 self.bullets pygame.sprite.Group()接下来修改事件处理让空格键触发发射。在_check_keydown_events方法中添加elif event.key pygame.K_SPACE: self._fire_bullet()并实现_fire_bullet方法def _fire_bullet(self): 创建一颗新子弹并将其加入子弹编组 # 限制屏幕上子弹数量避免过多导致游戏变卡 if len(self.bullets) self.settings.bullets_allowed: new_bullet Bullet(self) self.bullets.add(new_bullet)这里我们引入了一个新的设置bullets_allowed用于限制屏幕上同时存在的子弹数量防止玩家疯狂按空格键导致游戏性能下降。记得在settings.py中添加self.bullets_allowed 5。最后也是最关键的一步修改主循环中的更新和绘制逻辑。在run_game方法中我们已经在更新飞船self.ship.update()现在需要加上更新子弹编组。同时在_update_screen方法中绘制子弹。修改run_game中的更新部分self._check_events() self.ship.update() self.bullets.update() # 更新所有子弹的位置 self._update_screen()子弹的update()方法会被编组自动调用。但编组不会自动删除屏幕外的子弹我们需要在更新后清理。在self.bullets.update()后面添加# 删除已消失的子弹 for bullet in self.bullets.copy(): if bullet.rect.bottom 0: self.bullets.remove(bullet)这里为什么用self.bullets.copy()来遍历因为我们在遍历列表编组本质上是一个列表的同时要修改它删除元素直接遍历原列表会导致错误。复制一份副本进行遍历是安全的做法。修改_update_screen方法在绘制飞船后绘制所有子弹def _update_screen(self): self.screen.fill(self.settings.bg_color) self.ship.blitme() for bullet in self.bullets.sprites(): bullet.draw_bullet() pygame.display.flip()现在运行游戏用空格键发射子弹吧你会看到一排排红色矩形向上飞去飞出屏幕后自动消失。你已经成功运用了“编组”来管理大量相似的游戏对象这是游戏编程中的一个核心模式。5. 创造敌人生成外星人舰队并实现编队移动敌人是游戏的另一半灵魂。外星人舰队比子弹更复杂它们需要整齐地排成网格整体移动到达屏幕边缘后下移并反向。5.1 设计Alien类创建alien.pyimport pygame from pygame.sprite import Sprite class Alien(Sprite): 表示单个外星人的类 def __init__(self, ai_game): 初始化外星人并设置其起始位置 super().__init__() self.screen ai_game.screen self.settings ai_game.settings # 加载外星人图像并设置其rect属性 self.image pygame.image.load(‘images/alien.bmp’) self.rect self.image.get_rect() # 每个外星人最初都在屏幕左上角附近 self.rect.x self.rect.width self.rect.y self.rect.height # 存储外星人的精确水平位置 self.x float(self.rect.x) def update(self): 向右移动外星人 self.x self.settings.alien_speed * self.settings.fleet_direction self.rect.x int(self.x) def check_edges(self): 如果外星人位于屏幕边缘就返回True screen_rect self.screen.get_rect() return (self.rect.right screen_rect.right) or (self.rect.left 0)关键点解析fleet_direction这是一个新设置值为1或-1代表舰队当前的移动方向右或左。在update()中速度乘以方向实现了左右移动。check_edges()这个方法检查当前外星人是否碰到了屏幕左边缘或右边缘。主逻辑将利用这个方法来决定整个舰队是否需要改变方向并下移。在settings.py中添加外星人相关设置# 外星人设置 self.alien_speed 2.0 self.fleet_drop_speed 10 # 撞边后向下移动的速度 # fleet_direction为1表示向右移为-1表示向左移 self.fleet_direction 15.2 创建外星人舰队我们不直接在AlienInvasion中创建外星人而是创建一个专门的方法_create_fleet。在AlienInvasion.__init__中初始化一个外星人编组self.aliens pygame.sprite.Group()并在初始化后调用self._create_fleet()。_create_fleet方法是本项目的逻辑小难点。我们需要计算一行能放多少个外星人以及屏幕垂直空间能容纳多少行。def _create_fleet(self): 创建外星人群 # 创建一个外星人并计算一行可容纳多少个外星人 # 外星人的间距为外星人宽度 alien Alien(self) alien_width, alien_height alien.rect.size available_space_x self.settings.screen_width - (2 * alien_width) number_aliens_x available_space_x // (2 * alien_width) # 计算屏幕可容纳多少行外星人 ship_height self.ship.rect.height available_space_y (self.settings.screen_height - (3 * alien_height) - ship_height) number_rows available_space_y // (2 * alien_height) # 创建外星人群 for row_number in range(number_rows): for alien_number in range(number_aliens_x): self._create_alien(alien_number, row_number) def _create_alien(self, alien_number, row_number): 创建一个外星人并将其放在当前行 alien Alien(self) alien_width, alien_height alien.rect.size alien.x alien_width 2 * alien_width * alien_number alien.rect.x int(alien.x) alien.rect.y alien_height 2 * alien_height * row_number self.aliens.add(alien)计算逻辑拆解水平空间屏幕总宽度减去左右两边的边距各一个外星人宽度得到可用空间。每个外星人占据的横向空间是其宽度加上一个间隔这里间隔也设为一个外星人宽度。所以available_space_x // (2 * alien_width)得到每行的外星人数量。垂直空间屏幕总高度减去顶部边距三个外星人高度、减去底部留给飞船的空间一个飞船高度得到可用垂直空间。行间距同样设为一个外星人高度。available_space_y // (2 * alien_height)得到行数。定位在_create_alien中根据当前外星人在行中的序号alien_number和行号row_number计算出其精确的x, y坐标并赋值。这样一个整齐的外星人网格舰队就创建好了。别忘了在_update_screen方法中绘制外星人编组self.aliens.draw(self.screen)。Group.draw()方法会自动将组内每个精灵的image绘制到其rect指定的位置非常方便。5.3 实现舰队移动与转向逻辑舰队移动的逻辑是所有外星人一起横向移动当任何一个外星人碰到屏幕边缘整个舰队就向下移动一段距离然后横向移动方向取反。我们在AlienInvasion的主循环中更新外星人self.aliens.update()。但更新后需要检查是否需要改变舰队方向。我们添加一个方法_check_fleet_edges和_change_fleet_direction。def _check_fleet_edges(self): 有外星人到达边缘时采取相应的措施 for alien in self.aliens.sprites(): if alien.check_edges(): self._change_fleet_direction() break # 只要有一个碰到边缘整个舰队就转向所以找到后就跳出循环 def _change_fleet_direction(self): 将整群外星人下移并改变它们的移动方向 for alien in self.aliens.sprites(): alien.rect.y self.settings.fleet_drop_speed self.settings.fleet_direction * -1然后在主循环的更新部分在self.aliens.update()之后调用self._check_fleet_edges()。现在运行游戏你会看到一支外星人舰队在屏幕上方左右移动碰到边缘就整体下移并反向压迫感逐渐就来了。6. 碰撞检测与游戏逻辑射击、得分与结束游戏的核心互动来了子弹击中外星人以及外星人撞到飞船或屏幕底部。6.1 子弹与外星人的碰撞检测Pygame的精灵编组为我们提供了极其简便的碰撞检测方法。我们使用pygame.sprite.groupcollide()。在AlienInvasion.run_game的主循环中更新子弹和外星人之后添加碰撞检测# 检查是否有子弹击中了外星人 # 如果是就删除相应的子弹和外星人 collisions pygame.sprite.groupcollide( self.bullets, self.aliens, True, True)groupcollide()接受四个参数第一个编组第二个编组以及两个布尔值分别表示碰撞后是否删除第一个编组和第二个编组中的精灵。这里我们设置True, True意味着子弹和外星人相撞后双双消失。这个方法返回一个字典其中键是发生碰撞的子弹值是与该子弹发生碰撞的外星人列表。在这个简单版本中我们暂时不需要使用这个返回值但知道它的存在对后续扩展比如根据击中的外星人类型加分很有用。6.2 外星人与飞船的碰撞及到达屏幕底部游戏失败有两种情况外星人撞到飞船或者任何外星人到达了屏幕底部。首先检查外星人与飞船的碰撞。Pygame有pygame.sprite.spritecollideany()方法检查一个精灵是否与一个编组中的任何精灵发生碰撞。在AlienInvasion类中添加方法_check_ship_alien_collisions并在主循环中调用def _check_ship_alien_collisions(self): 响应外星人和飞船之间的碰撞 if pygame.sprite.spritecollideany(self.ship, self.aliens): self._ship_hit()_ship_hit()是处理飞船被撞后的逻辑我们稍后实现。其次检查是否有外星人到达屏幕底部。遍历外星人编组检查每个外星人的rect.bottom是否大于等于屏幕的rect.bottom。def _check_aliens_bottom(self): 检查是否有外星人到达了屏幕底端 screen_rect self.screen.get_rect() for alien in self.aliens.sprites(): if alien.rect.bottom screen_rect.bottom: # 像飞船被撞到一样处理 self._ship_hit() break # 只要有一个触底就触发事件并跳出循环同样在主循环中调用self._check_aliens_bottom()。6.3 实现_ship_hit()与游戏重置逻辑_ship_hit()是游戏逻辑的核心响应函数。当飞船被撞或外星人触底时我们需要清空子弹和外星人、创建新的舰队、将飞船重置到中央底部、然后暂停一小会儿让玩家有反应时间最后如果生命用尽则结束游戏。首先在Settings类中添加游戏动态设置和统计信息。我们修改Settings将静态设置和动态设置分开def initialize_dynamic_settings(self): 初始化随游戏进行而变化的设置 self.ship_speed 5.0 self.bullet_speed 10.0 self.alien_speed 2.0 # fleet_direction为1表示向右-1表示向左 self.fleet_direction 1在AlienInvasion.__init__中初始化设置后调用self.settings.initialize_dynamic_settings()。同时我们需要一个记录游戏状态的类创建game_stats.pyclass GameStats: 跟踪游戏统计信息 def __init__(self, ai_game): 初始化统计信息 self.settings ai_game.settings self.reset_stats() # 游戏刚启动时处于非活动状态等玩家点击才开始 self.game_active True # 我们先设为True方便测试后续可改为False def reset_stats(self): 初始化在游戏运行期间可能变化的统计信息 self.ships_left self.settings.ship_limit在settings.py中添加self.ship_limit 3表示玩家有3条命。回到AlienInvasion.__init__初始化统计信息self.stats GameStats(self)。现在可以实现_ship_hit()了def _ship_hit(self): 响应飞船被外星人撞到 if self.stats.ships_left 0: # 将ships_left减1 self.stats.ships_left - 1 # 清空余下的外星人和子弹 self.aliens.empty() self.bullets.empty() # 创建一群新的外星人并将飞船放到屏幕底端中央 self._create_fleet() self.ship.center_ship() # 暂停0.5秒让玩家有喘息之机 pygame.time.delay(500) else: self.stats.game_active False我们需要在Ship类中添加center_ship方法def center_ship(self): 将飞船放在屏幕底部中央 self.rect.midbottom self.screen_rect.midbottom self.x float(self.rect.x)同时在主循环开始更新和绘制前我们需要检查游戏是否仍处于活动状态def run_game(self): while True: self._check_events() if self.stats.game_active: self.ship.update() self._update_bullets() self._update_aliens() self._update_screen() self.clock.tick(60)我们把更新子弹和外星人的逻辑也封装成方法_update_bullets和_update_aliens让run_game更简洁。_update_bullets包含子弹更新、删除屏幕外子弹、碰撞检测。_update_aliens包含外星人更新、检查边缘、检查碰撞和触底。现在一个基本的游戏循环就完成了射击外星人被撞或让外星人触底会损失一条命三条命用完游戏结束game_active变为False停止更新游戏对象。7. 打磨与优化计分、难度提升与游戏体验基础功能完成后我们可以添加一些“糖霜”让游戏更像一个完整的作品。7.1 添加计分系统在GameStats类中添加得分属性并在reset_stats中初始化self.score 0。当子弹击中外星人时我们应该加分。修改_update_bullets方法中的碰撞检测部分遍历碰撞字典来加分collisions pygame.sprite.groupcollide( self.bullets, self.aliens, True, True) if collisions: for aliens in collisions.values(): self.stats.score self.settings.alien_points * len(aliens)这里假设每个外星人分值相同。我们可以在Settings的initialize_dynamic_settings中设置self.alien_points 50。为了在屏幕上显示分数我们需要创建一个记分牌类Scoreboard。这涉及到Pygame的字体渲染。由于篇幅所限这里简述步骤在Scoreboard的__init__中设置字体、颜色。创建一个方法prep_score将整数分数转换为渲染好的图像。创建一个方法show_score在_update_screen中调用将分数图像blit到屏幕上。在_update_bullets中加分后调用记分牌的prep_score更新图像。7.2 实现难度递增游戏不应该一成不变。当玩家清空一波外星人后下一波应该更快、更猛。我们可以在_update_bullets中检测如果外星人编组为空if not self.aliens:就调用一个方法_start_new_level。_start_new_level需要做清空现有的子弹。调用_create_fleet生成新舰队。提高游戏速度设置如self.settings.increase_speed()。在Settings类中添加increase_speed方法def increase_speed(self): 提高速度设置 self.ship_speed * self.speedup_scale self.bullet_speed * self.speedup_scale self.alien_speed * self.speedup_scale并在__init__中初始化速度缩放因子self.speedup_scale 1.1即每升一级速度提高10%。7.3 添加游戏开始与结束界面目前游戏启动即开始。我们可以改进为游戏启动时显示一个“Play”按钮游戏结束后显示“Game Over”并可以重新开始。这需要创建一个Button类用于绘制矩形和文字。在GameStats中将game_active初始值设为False。在_update_screen中根据game_active的状态决定是绘制游戏元素还是绘制按钮。修改事件检测当游戏非活动状态且用户点击了“Play”按钮区域时重置游戏统计信息、设置和状态将game_active设为True。这些优化步骤将大大提升项目的完整度和可玩性也让你接触到更丰富的Pygame功能如字体渲染、UI交互和游戏状态管理。从搭建窗口到实现移动、射击、碰撞、计分与难度提升这个《外星人入侵》项目几乎涵盖了一个简单2D游戏的所有核心要素。通过亲手实现它你不仅学会了Pygame的基本用法更重要的是理解了“游戏循环”、“事件驱动”、“面向对象设计”、“精灵编组管理”这些贯穿游戏开发始终的核心概念。下次当你看到更复杂的游戏时你会明白它们大多是在这个基础骨架上添加了更精美的皮肤、更复杂的规则和更丰富的交互而已。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门