让外星舰队移动

现在让我们让外星人舰队在屏幕上向右移动,直到到达边缘,然后让它下降一定的量并向另一个方向移动。 我们将继续这一运动,直到所有外星人都被击落、其中一个与飞船相撞、或者一个到达屏幕底部。 让我们首先让舰队向右移动。

向右移动外星舰队

为了移动外星人,我们将在 alien.py 中使用 update() 方法,我们将为外星人组中的每个外星人调用该方法。首先,添加一个设置来控制每个外星人的速度:

settings.py
def __init__(self):
    --snip--
    # Alien settings
    self.alien_speed = 1.0

然后使用这个设置在 alien.py 中实现 update() :

alien.py
def __init__(self, ai_game):
    """Initialize the alien and set its starting position."""
    super().__init__()
    self.screen = ai_game.screen
    self.settings = ai_game.settings
    --snip--

def update(self):
    """Move the alien to the right."""
    self.x += self.settings.alien_speed (1)
    self.rect.x = self.x (2)

我们在 __init__() 中创建一个 settings 参数,以便我们可以在 update() 中访问外星人的速度。每次我们更新外星人的位置时,我们都会将其向右移动 alien_speed 中存储的量。我们使用 self.x 属性来跟踪外星人的确切位置,该属性可以保存浮点值❶。然后我们使用 self.x 的值来更新外星人矩形的位置❷。

在主 while 循环中,我们已经调用了更新船舶和子弹位置的调用。 现在我们还将添加一个调用来更新每个外星人的位置:

alien_invasion.py
while True:
    self._check_events()
    self.ship.update()
    self._update_bullets()
    self._update_aliens() (1)
    self._update_screen()
    self.clock.tick(60)

我们将要编写一些代码来管理舰队的移动,因此我们创建一个名为 _update_aliens() 的新方法。我们会在子弹更新后更新外星人的位置,因为我们很快就会检查是否有子弹击中外星人。

将此方法放置在模块中的位置并不重要。但为了保持代码的组织性,我将其放置在 _update_bullets() 之后,以匹配 while 循环中方法调用的顺序。这是 _update_aliens() 的第一个版本:

alien_invasion.py
def _update_aliens(self):
    """Update the positions of all aliens in the fleet."""
    self.aliens.update()

我们在 aliens 组上使用 update() 方法,该方法调用每个外星人的 update() 方法。当您现在运行外星人入侵时,您应该看到舰队向右移动并消失在屏幕一侧。

创建表示外星舰队移动方向的设置

现在,我们将创建相关设置,使舰队在撞击屏幕右边缘时,向下并向左移动。下面是实现这一行为的方法:

settings.py
# Alien settings
self.alien_speed = 1.0
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1

设置舰队下降速度(fleet_drop_speed)可以控制每次外星人到达屏幕边缘时,舰队下降的速度。将这一速度与外星人的水平速度分开很有帮助,这样就可以独立调整这两个速度。

要实现舰队方向设置,我们可以使用 'left' 或 'right' 这样的文本值,但最终会使用 if-elif 语句测试舰队方向。相反,因为我们只需要处理两个方向,所以让我们使用值 1 和 -1 并在每次舰队改变方向时在它们之间切换。(使用数字也是有道理的,因为向右移动需要在每个外星人的 x 坐标值上做加法,而向左移动则需要从每个外星人的 x 坐标值上做减法)。

检查外星人是否到达了屏幕边缘

我们需要一个方法来检查异形是否位于任一边缘,我们还需要修改 update() 以允许每个外星人向适当的方向移动。这段代码是 Alien 类的一部分:

alien.py
def check_edges(self):

    """Return True if alien is at edge of screen."""
    screen_rect = self.screen.get_rect()
    return (self.rect.right >= screen_rect.right) or (self.rect.left <= 0) (1)

def update(self):
    """Move the alien right or left."""
    self.x += self.settings.alien_speed * self.settings.fleet_direction (2)
    self.rect.x = self.x

我们可以对任何异形调用新方法 check_edges(),查看它是位于左边缘还是右边缘。如果异形矩形的右属性大于或等于屏幕矩形的右属性,则该异形处于右边缘。如果它的左侧值小于或等于 0 ❶,则它位于左边缘。我们没有将条件测试放在 if 代码块中,而是直接将测试放在了返回语句中。如果异形位于右边缘或左边缘,该方法将返回 True;如果异形不在任一边缘,则返回 False。

我们修改 update() 方法,将异形的速度乘以舰队方向❷ 的值,从而允许向左或向右运动。如果舰队方向为 1,异形速度的值将加到异形的当前位置,使异形向右移动;如果舰队方向为 -1,异形速度的值将从异形的位置中减去,使异形向左移动。

向下移动外星舰队并改变移动方向

当一个外星人到达边缘时,整个舰队需要下降并改变方向。因此,我们需要在 AlienInvasion 中添加一些代码,因为我们要在这里检查是否有外星人处于左侧或右侧边缘。

我们将通过编写 _check_fleet_edges()_change_fleet_direction() 方法,然后修改 _update_aliens() 方法来实现这一点。我会把这些新方法放在 _create_alien() 之后,但这些方法在类中的位置并不重要。

alien_invasion.py
def _check_fleet_edges(self):
    """Respond appropriately if any aliens have reached a
    n edge."""
    for alien in self.aliens.sprites():  (1)
        if alien.check_edges():
            self._change_fleet_direction()  (2)
            break


def _change_fleet_direction(self):
    """Drop the entire fleet and change the fleet's direction."""
    for alien in self.aliens.sprites():
        alien.rect.y += self.settings.fleet_drop_speed (3)
    self.settings.fleet_direction *= -1

_check_fleet_edges() 中,我们会对舰队进行循环,并在每个外星人❶上调用 check_edges()。如果 check_edges() 返回 True,我们就知道某个外星人处于边缘位置,整个舰队需要改变方向;因此我们调用 _change_fleet_direction() 并跳出循环❷。在 _change_fleet_direction() 中,我们会循环查看所有异形,并使用舰队下降速度❸的设置将每个异形下降;然后我们会将舰队方向的当前值乘以 -1,从而改变其值。改变舰队方向的这一行并不是 for 循环的一部分。我们想改变每个外星人的垂直位置,但只想改变舰队的方向一次。

下面是对 _update_aliens() 的修改:

alien_invasion.py
def _update_aliens(self):
    """Check if the fleet is at an edge, then update positions."""
    self._check_fleet_edges()
    self.aliens.update()

我们修改了该方法,在更新每个外星人的位置之前调用 _check_fleet_edges()

现在运行游戏时,舰队应该会在屏幕边缘来回移动,并在每次撞到边缘时下降。现在,我们可以开始击落外星人,并留意是否有外星人击中飞船或到达屏幕底部。

亲身体验

13-3. 雨滴:找到雨滴的图像并创建一个雨滴网格。让雨滴落向屏幕底部,直至消失。

13-4. 稳定的雨滴:修改练习 13-3 中的代码,当一排雨滴从屏幕底部消失时,屏幕顶部会出现新的一排雨滴并开始下落。