射击外星人

我们建造了自己的飞船和外星人舰队,但当子弹射向外星人时,它们只是擦身而过,因为我们没有检查碰撞。在游戏编程中,当游戏元素重叠时就会发生碰撞。为了让子弹击落外星人,我们将使用 sprite.groupcollide() 函数来查找两个组中成员之间的碰撞。

检测子弹碰撞

我们希望在子弹击中外星人时立即知道,这样就能让外星人在被击中后立即消失。为此,我们将在更新所有子弹的位置后立即查找碰撞。

sprite.groupcollide() 函数将一组中每个元素的矩形与另一组中每个元素的矩形进行比较。在本例中,它会比较每个子弹的矩形和每个外星人的矩形,并返回一个包含发生碰撞的子弹和外星人的字典。字典中的每个键将是一颗子弹,相应的值将是被击中的外星人。(我们还将在 第 14 章 实施计分系统时使用该字典)。

_update_bullets() 的末尾添加以下代码,以检查子弹和外星人之间的碰撞:

alien_invasion.py
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 显示了一支被部分击落的舰队。

image 2023 12 04 10 30 15 617
Figure 1. Figure 13-5: We can shoot aliens!

制作更大的子弹进行测试

您只需运行游戏即可测试《外星入侵》的许多功能,但在普通版本的游戏中测试某些功能非常繁琐。例如,要多次击落屏幕上的每一个外星人,以测试你的代码是否能正确响应空舰队,这就很费事。

为了测试特定功能,您可以更改某些游戏设置,将注意力集中在特定区域。例如,你可以缩小屏幕,从而减少要击落的外星人数量,或者提高子弹速度,让自己一次获得大量子弹。

在测试 "外星人入侵" 时,我最喜欢的改动是使用非常宽的子弹,这种子弹在击中外星人后仍然有效(见图 13-6)。试着将子弹宽度设置为 300,甚至 3000,看看你能多快击落舰队!

image 2023 12 04 10 31 37 664
Figure 2. Figure 13-6: Extra-powerful bullets make some aspects of the game easier to test.

这些更改将帮助您更有效地测试游戏,并有可能激发出赋予玩家额外能力的想法。测试完成后,请记得将设置恢复正常。

重新填充舰队

外星人入侵的一个主要特点是外星人是无情的:每次舰队被摧毁,都会有新的舰队出现。

要在一支舰队被摧毁后让新的异形舰队出现,我们首先要检查异形组是否为空。如果是空的,我们就调用 _create_fleet()。我们将在 _update_bullets() 结束时执行这一检查,因为单个外星人就是在这里被摧毁的。

alien_invasion.py
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.pybullet_speed 的值来修改子弹的速度。在我的系统中,我会将 bullet_speed 的值调整为 2.5,这样子弹的速度就会稍快一些:

settings.py
# 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() 的时间过长,并简化进一步的开发工作。

亲身体验

13-5. 侧身射击第二部分:自练习 12-6《侧向射击》以来,我们已经取得了长足的进步。在本练习中,请尝试将 "侧向射击" 发展到与 "外星入侵" 相同的程度。添加一支外星人舰队,让它们侧向飞船移动。或者,编写代码将外星人随机放置在屏幕右侧的位置,然后将它们送向飞船。此外,还可以编写代码,让外星人在被击中后消失。