6.1 使外星人向右移动:
为了让外星人移动,需要使用alien.py中的方法update()
,且对外星人群中的每个外星人都调用它。首先,添加一个控制外星人速度的设置:
接着,需要使用这个设置来实现update()
:
每次更新外星人位置时,都将它向右移动,移动量为alien_speed_factor
的值。我们使用属性self.x
跟踪每个外星人的准确位置。然后,使用self.x
的值来更新外星人的rect
的位置(见28行处)。
在主 while 循环中已调用了更新飞船和子弹的方法,但现在还需更新每个外星人的位置:
最后,在文件game_functions.py末尾添加新函数update_aliens()
:
对编组aliens
调用方法update()
,这将自动对每个外星人调用方法update()
。如下图所示,如果现在运行这个游戏,会看到外星人群向右移,并逐渐在屏幕右边缘消失:
6.2 创建表示外星人移动方向的设置并检查外星人是否撞到了屏幕边缘:
下面来创建让外星人撞到屏幕右边缘后向下移动、再向左移动的设置:
在17-18行中,设置fleet_drop_speed
指定了有外星人撞到屏幕边缘时,外星人群向下移动的速度。将这个速度与水平速度分开是有好处的,这样就可以分别调整这两种速度了。鉴于只有两个可能的方向,我们使用值 1 和 -1 来表示它们,并在外星人群改变方向时在这两个值之间切换。另外,鉴于向右移动时需要增大每个外星人的 x 坐标,而向左移动时需要减小每个外星人的 x 坐标,使用数字来表示方向更合理。
现在需要编写一个方法来检查是否有外星人撞到了屏幕边缘,还需修改update()
,以让每个外星人都沿正确的方向移动:
我们可对任何外星人调用新方法check_edges()
,看看它是否位于屏幕左边缘或右边缘。如果外星人的rect
的right
属性大于或等于屏幕的rect
的right
属性,就说明外星人位于屏幕右边缘(见第28行处)。如果外星人的rect
的left
属性小于或等于0,就说明外星人位于屏幕左边缘(见第30行处)。
令笔者觉得巧妙的是: 通过修改方法update()
,将移动量设置为外星人速度和fleet_direction
的乘积,让外星人向左或向右移。如果fleet_direction
为 1,就将外星人当前的 x 坐标增大alien_speed_factor
,从而将外星人向右移;如果fleet_direction
为 -1 ,就将外星人当前的 x 坐标减去alien_speed_factor
,从而将外星人向左移。
6.3 向下移动外星人群并改变其移动方向:
有外星人到达屏幕边缘时,需要将整群外星人下移,并改变它们的移动方向。我们需要对game_functions.py做修改,因为我们要在这里检查是否有外星人到达了左边缘或右边缘。为此,我们编写函数check_fleet_edges()
和change_fleet_direction()
,并对update_aliens()
进行修改:
- 在
check_fleet_edges()
中,我们遍历外星人群,并对其中的每个外星人调用check_edges()
(见第91行)。如果check_edges()
返回 True ,我们就知道相应的外星人位于屏幕边缘,需要改变外星人群的方向,因此我们调用change_fleet_direction()
并退出循环。 - 在
change_fleet_direction()
中,我们遍历所有外星人,将每个外星人下移fleet_drop_speed
设置的值(见第98行);然后,将fleet_direction
的值修改为其当前值与 -1 的乘积。 - 我们修改了函数
update_aliens()
,在其中通过调用check_fleet_edges()
来确定是否有外星人位于屏幕边缘。
现在,函数update_aliens()
包含形参ai_settings
,因此我们调用它时指定了与ai_settings
对应的实参:
如下图所示,如果现在运行这个游戏,外星人群将在屏幕上来回移动,并在抵达屏幕边缘后向下移动:
7.1 发射子弹射击外星人:
我们创建了飞船和外星人群,但子弹击中外星人时,将穿过外星人,因为我们还没有检查碰撞。在游戏编程中,碰撞指的是游戏元素重叠在一起。要让子弹能够击落外星人,我们将使用sprite.groupcollide()
检测两个编组的成员之间的碰撞。
子弹击中外星人时,我们要马上知道,以便碰撞发生后让外星人立即消失。为此,我们将在更新子弹的位置后立即检测碰撞。 方法sprite.groupcollide()
将每颗子弹的rect
同每个外星人的rect
进行比较,并返回一个字典,其中包含发生了碰撞的子弹和外星人。在这个字典中,每个键都是一颗子弹,而相应的值都是被击中的外星人。
在函数update_bullets()
中,使用下面的代码来检查碰撞:
新增的这行代码遍历编组bullets
中的每颗子弹(第53行处),再遍历编组aliens
中的每个外星人。每当有子弹和外星人的rect
重叠时,groupcollide()
就在它返回的字典中添加一个键-值对。值得注意的是,两个实参 True 告诉Pygame删除发生碰撞的子弹和外星人。
我们调用update_bullets()
时,传递了实参aliens
:
如果此时运行这个游戏,被击中的外星人将消失。如图所示,其中有一部分外星人被击落:
7.2 生成新的外星人群:
要在外星人群被消灭后又显示一群外星人,首先需要检查编组aliens
是否为空。如果为空,就调用create_fleet()
。我们将在update_bullets()
中执行这种检查,因为外星人都是在这里被消灭的:
在第55行处,我们检查编组aliens
是否为空。如果是,就使用方法empty()
删除编组中余下的所有精灵,从而删除现有的所有子弹。我们还调用了create_fleet()
,再次在屏幕上显示一群外星人。 现在,update_bullets()
的定义包含额外的形参ai_settings
、screen
和ship
,因此我们需要更新alien_invasion.py中对update_bullets()
的调用:
现在,如下图所示,当外星人群被消灭干净后,将立刻出现一个新的外星人群: