射击外星人
我们建造了自己的飞船和外星人舰队,但当子弹射向外星人时,它们只是擦身而过,因为我们没有检查碰撞。在游戏编程中,当游戏元素重叠时就会发生碰撞。为了让子弹击落外星人,我们将使用 sprite.groupcollide()
函数来查找两个组中成员之间的碰撞。
检测子弹碰撞
我们希望在子弹击中外星人时立即知道,这样就能让外星人在被击中后立即消失。为此,我们将在更新所有子弹的位置后立即查找碰撞。
sprite.groupcollide()
函数将一组中每个元素的矩形与另一组中每个元素的矩形进行比较。在本例中,它会比较每个子弹的矩形和每个外星人的矩形,并返回一个包含发生碰撞的子弹和外星人的字典。字典中的每个键将是一颗子弹,相应的值将是被击中的外星人。(我们还将在 第 14 章 实施计分系统时使用该字典)。
在 _update_bullets()
的末尾添加以下代码,以检查子弹和外星人之间的碰撞:
def _update_bullets(self):
"""Update position of bullets and get rid of old bullets."""
--snip--
# Check for any bullets that have hit aliens.
# If so, get rid of the bullet and the alien.
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
我们添加的新代码会比较 self.bullets
中所有子弹和 self.aliens
中所有外星人的位置,并找出任何重叠的位置。每当子弹和外星人的矩形重叠时,groupcollide()
就会在它返回的字典中添加一个键值对。两个 True
参数告诉 Pygame 删除发生碰撞的子弹和外星人。(要制作一颗能飞向屏幕顶端、摧毁路径上所有外星人的高能子弹,可以将第一个布尔参数设置为 False
,而将第二个布尔参数设置为 True
。被击中的外星人会消失,但所有子弹都会保持激活状态,直到它们从屏幕顶部消失)。
现在运行 "外星入侵" 时,被击中的外星人应该会消失。图 13-5 显示了一支被部分击落的舰队。

制作更大的子弹进行测试
您只需运行游戏即可测试《外星入侵》的许多功能,但在普通版本的游戏中测试某些功能非常繁琐。例如,要多次击落屏幕上的每一个外星人,以测试你的代码是否能正确响应空舰队,这就很费事。
为了测试特定功能,您可以更改某些游戏设置,将注意力集中在特定区域。例如,你可以缩小屏幕,从而减少要击落的外星人数量,或者提高子弹速度,让自己一次获得大量子弹。
在测试 "外星人入侵" 时,我最喜欢的改动是使用非常宽的子弹,这种子弹在击中外星人后仍然有效(见图 13-6)。试着将子弹宽度设置为 300,甚至 3000,看看你能多快击落舰队!

这些更改将帮助您更有效地测试游戏,并有可能激发出赋予玩家额外能力的想法。测试完成后,请记得将设置恢复正常。
重新填充舰队
外星人入侵的一个主要特点是外星人是无情的:每次舰队被摧毁,都会有新的舰队出现。
要在一支舰队被摧毁后让新的异形舰队出现,我们首先要检查异形组是否为空。如果是空的,我们就调用 _create_fleet()
。我们将在 _update_bullets()
结束时执行这一检查,因为单个外星人就是在这里被摧毁的。
def _update_bullets(self):
--snip--
if not self.aliens: (1)
# Destroy existing bullets and create new fleet.
self.bullets.empty() (2)
self._create_fleet()
我们检查异类组是否为空❶。空组的值为 False
,因此这是检查组是否为空的简单方法。如果是空的,我们就使用 empty()
方法来删除任何现有的子弹,该方法会删除一个组❷ 中所有剩余的精灵。我们还可以调用 _create_fleet()
,让外星人再次出现在屏幕上。
现在,只要摧毁当前舰队,就会出现新的舰队。
加快子弹速度
如果您尝试过在游戏当前状态下向外星人开火,您可能会发现子弹的飞行速度并不适合游戏。它们可能太慢了一点,也可能太快了一点。这时,你可以修改设置,让游戏更有趣。请记住,游戏的速度会逐渐变快,所以一开始不要让游戏太快。
我们可以通过调整 settings.py 中 bullet_speed
的值来修改子弹的速度。在我的系统中,我会将 bullet_speed
的值调整为 2.5,这样子弹的速度就会稍快一些:
# Bullet settings
self.bullet_speed = 2.5
self.bullet_width = 3
--snip--
该设置的最佳值取决于您的游戏经验,因此请找到一个适合您的值。您还可以调整其他设置。
重构 _update_bullets()
让我们重构一下 _update_bullets()
,这样它就不会执行那么多不同的任务了。我们将把处理 Bulletalien
碰撞的代码移到一个单独的方法中:
def _update_bullets(self):
--snip--
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
我们创建了一个新方法 _check_bullet_alien_collisions()
,用于查找子弹与外星人之间的碰撞,并在整个舰队被摧毁时做出适当响应。这样做可以避免 _update_bullets()
的时间过长,并简化进一步的开发工作。