python项目1--【外星人入侵游戏】之外星人来了
python项目1--【外星人入侵游戏】之外星人来了
本篇将在游戏《外星人入侵》中添加外星人。我们将首先在屏幕上边缘附近添加一个外星人,再生成一群外星人。然后让这群外星人向两边和下面移动,并删除被子弹击中的外星人。最后,显示玩家拥有的飞船数量,并在玩家的飞船用完后结束游戏。
通过阅读本篇,你将更深入地了解Pygame和大型项目管理,还将学习如何检测游戏对象之间的碰撞,如子弹和外星人之间的碰撞。检测碰撞有助于定义游戏元素之间的交互。例如,可以将角色限定在迷宫墙壁之内,或者在两个角色之间传球。我们将不时查看游戏开发计划,确保变成工作不偏离轨道。着手编写在屏幕上添加一群外星人的代码前,先来回顾一下这个项目,并更新开发计划。
一、项目回顾
开发大型项目时,要在进入每个开发阶段之前回顾一下开发计划,搞清楚接下来要通过编写代码来完成哪些任务。本篇涉及一下内容。
- 研究既有代码,确定实现新功能前是否要重构。
- 在屏幕左上角添加一个外星人,并指定合适的编剧。
- 根据第一个外星人的边距和屏幕尺寸计算屏幕上可容纳多少个外星人。编写一个循环来创建一系列外星人,使其填满屏幕的上半部分。
- 让外星人群向两边和下方移动,移到外星人被全部击落、有外星人撞到飞船或有外星人抵达屏幕底端。如果整群外星人都被击落,将再创建一群外星人。如果有外星人撞到了飞船或抵达屏幕底端,将销毁飞船并创建一群外星人。
- 限制玩家可用的飞船数量,当配给的飞船用完之后,游戏将结束。
我们将在实现功能的同时完善这个计划,但就目前而言,该计划已足够详尽。
在项目中添加新功能前,还应审核既有代码。没进入一个新阶段,项目通常会更复杂。因此最好对混乱或低效的代码进行清理。我们一直在不断重构,因此当前没有需要重构的代码。
二、创建第一个外星人
在屏幕上防止外星人与防止飞船类似。每个外星人的行为都有Alien
类控制,我们将像创建Ship
类那样创建这个类。出于简化考虑,也将使用位图来表示外星人。你可以自己寻找表示外星人的图像,也可以使用下图。这幅图像的背景为灰色,与屏幕背景色一致。请务必将选择的图像文件保存到文件夹images
中。
1. 创建Alien类
下面来编写Alien
类并将其保存为文件alien.py
:
除位置不同外,这个类的大部分代码与Ship
类相似。每个外星人最初都位于屏幕左上角附近。将每个外星人的左边距都设置为外星人的宽度,并将上边距设置为外星人的高度,这样更容易看清。我们主要关心的是外星人的水平速度,因此精确地记录了每个外星人的水平位置。
Alien
类不需要一个在屏幕上绘制外星人的方法,因为我们将使用一个Pygame编组方法。自动在屏幕上绘制编组中的所有元素。
2. 创建Alien实例
要让第一个外星人在屏幕上现身,需要创建一个Alien
实例。这属于设置工作,因此将把这些代码放在AlienInvasion
类的方法__init__()
末尾。我们最终会创建一群外星人,涉及的工作量不少,因此将新建一个名为_create_fleet()
的辅助方法。
在类中,方法的排列顺序无关紧要,只要按统一的标准排列就行。我们将把_create_fleet()
放在update_screen()
前面,不过放在AlienInvasion
类的任何地方其实都可行。首先,需要导入Alien
类。
下面是alien_invasion,py
中修改后的import
语句:
下面是修改后的方法__init__()
:
创建了一个用于存储外星人群的编组,还调用了接下来将编写的方法_create_fleet()
。下面是新编写的方法_create_fleet()
:
在这个方法中,创建了一个Alien
实例,再将其添加到用于存储外星人群的编组中。外星人默认放在屏幕左上角附近。对第一个外星人来说,这样的位置非常合适。
要让外星人现身,需要在_update_screen()
中对外星人编组调用方法draw()
:
对编组调用draw()
时,Pygame将把编组中的每个元素绘制到属性rect
指定的位置。方法draw()
接受一个参数,这个参数指定了要将编组中的元素绘制到哪个surface
上。下图显示了在屏幕上现身的第一个外星人。
第一个外星人正确地现身了,下面来编写绘制一群外星人的代码。
三、创建一群外星人
要绘制一群外星人,需要确定一行能容纳多少个外星人以及要绘制多少行。我们将首先计算外星人的水平间距并创建一行外星人,再确定可用的垂直空间并创建整群外星人。
1. 确定一行可容纳多少个外星人
为确定一行可容纳多少个外星人,来看看可用的水平空间有多大。屏幕宽度存储在settings.screen_width
中,但需要在屏幕两边都留下一定边距(将其设置为外星人的宽度)。因为有两个边距,所以可用于防止外星人的水平空间为屏幕宽度减去外星人宽度的两倍:
available_space_x = settings.screen_width - (2 * alien_width)
还需要在外星人之间留出一定的空间,不妨将其定为外星人的宽度。因此,显示一个外星人所需的水平空间为外星人宽度的两倍:一个宽度用于防止外星人,另一个宽度为外星人右边的空白区域。为确定一行可容纳多少个外星人,将可用空间除以外星人宽度的两倍。我们使用整除(floor division)运算符//,它将两个数相除并丢弃余数。让我们得到一个表示外星人个数的证书。
number_aliens_x = available_space_x // (2 * alien_width)
我们将在创建外星人群时使用这些公式。
注意:令人欣慰的是,在程序中执行计算时,无需在一开始确定公式是正确的,而是可以尝试运行程序,看看结果是否符合预期。即便是在最坏的情况下,也只是屏幕上显示的外星人太多或太少。随后可根据在屏幕上看到的情况调整计算公式。
2. 创建一行外星人
现在可以创建整行外星人了。由于创建单个外星人的代码管用,我们重写_create_fleet()
使其创建一行外星人:
这些代码大多在前面详细介绍过。为防止外星人,需要知道外星人的宽度和高度,因此在执行计算前,创建一个外星人。这个外星人不是外星人群的成员,因此没有将其加入编组aliens
中。接着,从外星人的rect
属性中获取外星人宽度,并将这个值存储到alien_width
中,以免反复访问属性rect
。然后,计算可用于防止外星人的水平空间以及其中可容纳多少个外星人。
接下来,编写一个循环,从零数到要创建的外星人数。在这个循环中,创建一个新的外星人,并通过设置x坐标将其加入当前行。将每个外星人都往右推一个外星人宽度。接下来,讲外星人宽度乘以2,得到每个外星人占据的空间(其中包括右边的空包区域),再据此计算当前外星人在当前行的位置。我们使用外星人的属性x来设置其rect
的位置。最后,将每个新创建的外星人都添加到编组aliens
中。
如果现在运行这个游戏,将看到第一行外星人,如下图所示:
这行外星人在屏幕上稍微偏向了左边、这实际上是有好处的,因为后面将让外星人群往右移,触及屏幕边缘后稍微往下移,再往左移,依此类推。就像经典游戏《太空入侵者》,相比于只往下移,这种移动方式更为有趣。我们将让外星人群不断这样移动,直到所有外星人都被击落,或者有外星人撞上飞船或抵达屏幕底端。
注意:根据所选择的屏幕宽度,在你的系统中,第一个外星人的位置可能稍有不同。
3. 重构_create_fleet()
倘若只需使用前面的代码就能创建外星人群,也许应该让_create_fleet()
保持原样,但鉴于创建外星人群的工作还未完成,我们稍微整理一下这个方法。为此,添加辅助方法_create_alien()
,并在_create_fleet()
中调用它:
除self
外,方法_create_alien()
还接受另一个参数,既要创建的外星人的编号。该方法的代码与_create_fleet()
相同,但在内部获取外星人宽度,而不是将其作为参数传入。这样重构后,添加新行进而创建整群外星人将更容易。
4. 添加行
要创建外星人群,需要计算屏幕可容纳多少行,并将创建一行外星人的循环重复执行响应的次数。为计算可容纳的行数,要先计算可用的垂直空间:用屏幕高度减去第一行外星人的上边距(外星人高度)、飞船的高度以及外星人群最初与飞船之间的距离(外星人高度的两倍):
avaliable_space_y = settings.screen_height - (3 * alien_height) -ship_height
浙江在飞船上方留出一定的空白区域,给玩家留出射杀外星人的时间。
每行下方都要留出一定的空白区域,不妨将其设置为外星人的高度。为计算可容纳的行数,将可用的垂直空间除以外星人高度的两倍。我们使用整除,因为行数只能是整数。(同样,如果这样的计算不对,我们马上就能发现,继而将间距调整为合理的值。)
number_rows = available_space_y // (2 * alien_height)
知道可容纳多少行之后,便可重复执行创建一行外星人的代码了:
需要知道外星人的宽度和高度,因此使用了属性size
。该属性是一个元组,包含rect
对象的宽度和高度。未计算屏幕可容纳多少行外星人,在计算available_space_x
的代码后面添加了计算available_space_y
的代码。此处将计算公式用圆括号括起来,以便将代码分成两行,遵循每行不超过79字符的建议。
为创建多行外星人,使用了两个嵌套在一起的循环:一个外部循环和一个内部循环。内部循环创建一行外星人,而外部循环从零数到要创建的外星人行数:Python将重复执行创建单行外星人的代码,重复次数为number_rows
。
为嵌套循环,编写了一个新的for
循环,并缩进了要重复执行的代码。现在调用_create_alien()
时,传递了一个表示行号的实参。将每行都沿屏幕一次向下放置,
在_create_alien()
的定义中,需要一个用于存储行号的形参。在_create_alien()
中,修改外星人的y坐标并在第一行外星人上方留出与外星人等高的空白区域。相邻外星人行的y坐标相差外星人高度的两倍,因此将外星人高度乘以2,再乘以行号。第一行的行号为0.因此第一行的垂直位置不变,而其他行都沿屏幕依次向下放置。
如果现在运行这个游戏,将看到一群外星人,如下图所示:
下面就让外星人群动起来!
四、让外星人群移动
下面来让外星人群在屏幕上向右移动,撞到屏幕边缘后下移一定的量,再沿相反的方向移动。我们将不断移动所有的外星人,直到外星人被全部消灭,或者有外星人撞上飞船或抵达屏幕底端。下面先让外星人向右移动。
1. 向右移动外星人群
为移动外星人群,将使用alien.py
中的方法update()
。对于外星人群众的每个外星人,都要调用它。首先,添加一个控制外星人速度的设置:
再使用这个设置来实现update()
:
在__init__()
添加了属性settings
,以便能够在update()
中访问外星人的速度。每次更新外星人时,都将它向右移动,移动量为alien_speed
的值。我们使用属性self.x
跟踪每个外星人的准确位置,该属性可存储小数值。然后,使用self.x
的值来更新外星人的rect
的位置。
主while
循环中已调用了更新飞船和子弹的方法,现在还需更调用更新每个外星人位置的方法:
需要编写一些代码来管理外星人群的移动,因此新建一个名为_update_aliens()
的方法。我们在更新子弹后再更新外星人的位置,因为稍后要检查是否有子弹击中了外星人。
将这个方法放在模块的什么地方都无关紧要,但为确保代码组织有序,我将它放在方法_update_bullets()
后面,以便于while
循环中的调用顺序一致。下面是_update_aliens()
的第一版:
对编组调用方法update()
,这将自动对每个外星人调用方法update()
。如果现在运行这个游戏,你将看到外星人群向右移动,并在屏幕右边缘消失。
2. 创建表示外星人移动方向的设置
下面来创建外星人撞到屏幕右边缘后向下移动、再向左移动的设置。实现这种行为的代码如下:
设置fleet_drop_speed
指定有外星人撞到屏幕边缘时,外星人群向下移动的速度。将这个速度与水平速度分开是有好处的,便于分别调整这两个速度。
要实现设置fleet_direction
,可将其设置为文本值,如left
或right
,但这样就必须编写if-elif
语句来检查外星人群的移动方向。鉴于只有两个可能的方向,我们使用1
和-1
来表示,并在外星人群改变方向时在这两个值之间切换。**向右移时需要增大每个外星人的
x坐标,而向左移时需要减小每个外星人的
x坐标,因此使用数字来表示方向十分合理**
。
3. 检查外星人是否撞到了屏幕边缘
现在需要编写一个方法来检查外星人是否撞到了屏幕边缘,还需修改update()
让每个外星人都沿正确的方向移动。这些代码位于Alien
类中:
可对任意外星人调用新方法check_edges()
,看其是否位于屏幕左边缘或右边缘。如果外星人的rect
的属性right
大于或等于屏幕的rect
的right
属性,就说明外星人位于屏幕的右边缘;如果外星人的rect
的left
属性小于或等于0
,就说明外星人位于屏幕左边缘。
我们修改方法uodate()
,将移动量设置为外星人速度和fleet_direction
的乘积,让外星人向左或向右移动。如果fleet_diretion
为1
,就将外星人的当前x
坐标增大alien_speed
,从而将外星人向右移;如果fleet_direction
为-1
,就将外星人的当前x
坐标减去alien_speed
,从而将外星人向左移。
4. 向下移动外星人群并改变移动方向
有外星人到达屏幕边缘时,需要将整群外星人下移,并改变它们的移动方向。为此,需要在AlienInvasion
中添加一些代码,因为要在这里检查是否有外星人到达了左边缘或右边缘。我们编写方法_check_fleet_edges()
和_check_fleet_direction()
,并且修改_update_aliens()
。这些新方法将放在_create_alien()
后面,但其实放在AlienInvasion
类中的什么位置都无关紧要:
在_check_fleet_edges()
中,遍历外星人群并对其中的每个外星人调用check_edges()
。如果check_edges()
返回True
,就表明响应的外星人位于屏幕边缘,需要改变外星人群的方向,因此调用_change_fleet_direction()
并退出循环。在_change_fleet_direction()
中,遍历所有外星人,将每个外星人下移设置fleet_drop_speed
的值。然后,将fleet_direction
的值改为其当前值与-1
的乘积。调整外星人群移动方向的代码行没有包含在for
循环中,因为我们要调整每个外星人的垂直位置,但只想调整外星人群移动方向一次。
下面显示了对_update_aliens()
所做的修改:
我们将方法_update_aliens()
修改成了先调用_check_fleet_edges()
,再更新每个外星人的位置。
如果现在运行这个游戏,外星人群在屏幕上来回移动,并在抵达屏幕边缘后向下移动。现在可以开始射杀外星人,并检查是否有外星人撞到飞船后抵达屏幕底端。
五、射杀外星人
我们创建了飞船和外星人群,但子弹击中外星人时将穿过外星人,因为还没有检查碰撞。在游戏编程中,碰撞指的是游戏元素重叠在一起。要让子弹能够击落外星人,我们将使用sprite.groupcollide()
检测两个编组的成员之间的碰撞。
1. 检测子弹与外星人的碰撞
子弹击中外星人时,我们需要马上知道,以便碰撞发生后让子弹立即消失。为此,我们将在更新子弹的位置后立即检测碰撞。
函数sprite.gtoupcollide()
将一个编组中每个元素的rect
同另一个编组中每个元素的rect
进行比较。在这里,是将每颗子弹的rect
同每个外星人的rect
进行比较,并返回一个字典,其中包含发生了碰撞的子弹和外星人。在这个字典中,每个键都是一颗子弹,而关联的值是被该子弹击中的外星人。
在方法_update_bullets()
末尾,添加如下检查子弹和外星人碰撞的代码:
这些新增的代码将self.bullets
中所有的子弹都与self.aliens
中所有的外星人进行比较,看它们是否重叠在一起。每当有子弹和外星人的rect
重叠时,groupcollide()
就在它返回的字典中添加一个键值对。两个实参True
让Pygame删除发生碰撞的子弹和外星人。(要模拟能够飞行到屏幕顶端、消灭几种的每个外星人的高能子弹,可将第一个布尔实参设置为False
,并保留第二个布尔参数为True
。这样被击中的外星人将消失,但所有的子弹都始终有效,直到抵达屏幕顶端后消失。)
如果此时运行这个游戏,被击中的外星人将消失。如下图,有些外星人被射杀了。
2. 为测试创建大子弹
只需运行这个游戏就可测试很多功能,但有些功能在正常情况下测试起来比较繁琐。例如,要测试代码能否正确处理外星人编组为空的情形,需要花很长时间将屏幕上的外星人全部射杀。
测试有些功能时,可以修改游戏的某些设置,以便能够专注于游戏的特定方面。例如,可以缩小屏幕以减少需要射杀的外星人数量,也可以提高子弹的速度,以便能够在单位时间内发射大量子弹。
测试这个游戏时,我喜欢做的一项修改是,增大子弹的尺寸并使其在几种外星人后依然有效,如下图所示。请尝试将bullet_width
设置为300
乃至3000
,看看将所有外星人全部射杀有多快!
这样的修改可提高测试效率,还可能激发出如何赋予玩家更大威力的vip氪金体验。(完成测试后,别忘了将设置恢复正常。)
3. 生成新的外星人群
这个游戏的一个重要特点是,外星人无穷无尽:一群外星人被消灭后,又会出现另一群外星人。要在一群外星人被消灭后,又会出现另一群外星人。
要在一群外星人被消灭后再显示一群外星人,首先需要检查编组aliens
是否为空。如果是,就调用_create_fleet()
。我们将在_update_bullets()
末尾执行这项任务,因为外星人都是在这里被消灭的:
首先,检查编组aliens
是否为空。空编组相当于False
,因此这是一种检查编组是否为空的简单方式。如果编组aliens
为空,就使用方法empty()
删除编组中余下的所有精灵,从而删除现有的所有子弹。我们还调用了_create_fleet()
,在屏幕上重新显示一群外星人。
现在,当前这群外星人被消灭干净后,将立刻出现一群新的外星人。
4. 提高子弹的速度
如果现在尝试在游戏中射杀外星人,可能会发现子弹的速度不太合适(有点块或有点慢),游戏感不好。当前,可通过修改设置让这块游戏更有意思、更好玩。
要修改子弹的速度,可调整settings.py
中bullet_speed
的值。在我的系统中,我把bullet_speed
的值调整到5.0
,让子弹的速度快些:
这项设置的最佳值取决于你使用的系统的速度,请找出适合自己的值。你也可以调整其他设置。
5. 重构_update_bullets()
下面来重构_update_bullets()
,使其不再执行那么多任务。为此,将处理子弹和外星人碰撞的代码移到一个独立的方法中:
我们创建了一个新方法_check_bullet_alien_collisions()
,用于检测子弹和外星人之间的碰撞,并在整群外星人并消灭干净时采取相应的措施。这能避免_update_bullets()
过长,简化了后续开发工作。
六、结束游戏
如果玩家根本不会输,游戏还有什么趣味和挑战性可言?如果玩家没能在足够短的时间内将整群外星人消灭干净,导致有外星人撞到了飞船或抵达屏幕底端,飞船将被摧毁。与此同时,限制玩家可使用的飞船数,在玩家用光所有的飞船后,游戏将结束。
1. 检查外星人和飞船碰撞
首先检查外星人和飞船之间的碰撞,以便在外星人撞上飞船时做出合适的响应。为此,在AlienInvasion
中更新每个外星人的位置后,立即检测外星人和飞船之间的碰撞:
函数spritecollideany()
接受两个实参:一个精灵和一个编组。它检查编组是否有成员与精灵发生了碰撞,并在找到与精灵发生碰撞的成员后停止遍历编组。在这里,它遍历编组aliens
,并返回找到的第一个与飞船发生碰撞的外星人。
如果没有发生碰撞,spritecollideany()
将返回None
,因此if
代码块不会执行。如果找到了与飞船发生碰撞的外星人,它就返回这个外星人,因此if
代码块将执行:打印"Ship hit!!!"
。有外星人撞到飞船时,需要执行很多任务:删除余下的外星人和子弹,让飞船重新居中,以及创建一群新的外星人。编写完成这些任务的代码之前,需要确定检测外星人和飞船碰撞的方法是否可行。为此,最简单的方式就是调用函数print()
。
现在如果运行这个游戏,则每当有外星人撞到飞船时,终端窗口都将显示"Ship hit!!!"
。测试这项功能时,请将alien_drop_speed
设置为较大的值,如50
或100
,这样外星人将更快地撞到飞船。
2. 响应外星人和飞船碰撞
现在需要确定当外星人与飞船飞升碰撞时该做些什么。我们不销毁Ship
实例并创建新的,而是通过跟踪游戏的统计信息来记录飞船被撞了多少次(跟踪统计信息还有助于计分)。
下面来编写一个用于跟踪游戏统计信息的新类GameStats
,并将其保存为文件game_stats.py
:
在游戏运行期间,只创建一个GameStats
实例,但每当玩家开始新游戏时,需要充值一些统计信息。为此,在方法reset_stats()
中初始化大部分统计信息,而不是在__init__()
中直接初始化。我们在__init__()
中调用这个方法,这样创建GameStats
实例时将妥善地设置这些统计信息,在玩家开始新游戏时也能调用reset_stats()
。
当前,只有一项统计信息ships_left
,其值在游戏运行期间不断变化。一开始玩家拥有的飞船数存储在settings.py
的ship_limit
中:
还需对alien_invasion.py
做些修改,以创建一个GameStats
实例。首先,更新这个文件开头的import
语句:
从Python标准库的模块time
中导入函数sleep()
,以便在飞船被外星人撞到后让游戏暂停片刻。我们还导入了GameStats
。
接下来,在__init__()
中创建一个GameStats
实例:
在创建游戏窗口后、定义注入飞船等其他游戏元素前,创建一个GameStats
实例。
有外星人撞到飞船时,将余下的飞船数减1
,创建一群新的外星人,并将飞船重新放到屏幕底端的中央。另外,让游戏暂停片刻,让玩家在新外星人群出现前注意到发生了碰撞并将重新创建外星人群。
下面将实现这些功能的大部分代码放到新方法_ship_hit()
中。在_update_aliens()
,将在有外星人撞到飞船时调用这个方法:
新方法_ship_hit()
在飞船被外星人撞到时做出响应。在这个方法中,将余下的飞船数减1
,再清空编组aliens
和bullets
。
接下来,创建一群新的外星人,并将飞船居中。(稍后将在Ship
类中添加方法center_ship()
。)最后,在更新所有元素后(但在将修改显示到屏幕前)暂停,让玩家知道飞船被撞到了。这里的函数调用sleep()
让游戏暂停半秒钟,让玩家能够看到外星人撞到了飞船。函数sleep()
执行完毕后,将接着执行方法_update_screen()
,将新的外星人群绘制到屏幕上。
在_update_aliens()
中,当有外星人撞到飞船时,不调用函数print()
,而调用_ship_hit()
:
下面是新方法center_ship()
,请将其添加到ship.py
的末尾:
这里像__init__()
中那样让飞船在屏幕底端居中。让飞船在屏幕底端居中后,重置用于跟踪飞船确切位置的属性rect.x
。
注意:我们根本没有创建多艘飞船。在整个游戏运行期间,只创建了一个飞船实例,并在该飞船被撞到时将其居中。统计信息ships_left
指出玩家是否用完了所有的飞船。
请运行这个游戏,射杀几个外星人,并让一个外星人撞到飞船。游戏暂停片刻后,将出现一群新的外星人,而飞船将在屏幕底端居中。
3. 有外星人到达屏幕底端
如果有外星人到达屏幕底端,我们将像有外星人撞到飞船那样做出响应。为检测这种情况,在alien_invasion.py
中添加一个新方法:
方法_check_aliens_bottom()
检查是都有外星人到达了屏幕底端。到达屏幕底端后,外星人的属性rect.bottom
大于或等于屏幕的属性rect.bottom
。如果有外星人到达屏幕底端,就调用_ship_hit()
。只要检测到一个外星人到达屏幕底端,就无须检查其他外星人了,因此在调用_ship_hit()
后退出循环。
我们在_update_aliens()
中调用_check_aliens_bottom()
:
在更新所有外星人的位置并检测是否有外星人和飞船碰撞后调用_check_alians_bottom()
。现在,每当有外星人撞到飞船或抵达屏幕底端时,都将出现一群新的外星人。
4. 游戏结束
现在这个游戏看起来更完整了,但它永远都不会结束,只是ships_left
不断变成越来越少的负数。下面再GameStats
中添加一个作为标志的属性game_active
,以便在玩家的飞船用完后结束游戏。首先,在GameStats
类的方法__init__()
末尾设置这个标志:
接下来在_ship_hit()
中添加代码,在玩家的飞船用完后将game_active
设置为False
:
_ship_hit()
的大部分代码没有变。我们将原来的代码都移到了一个if
语句块中,它检查玩家是否至少还有一艘飞船。如果是,就创建一群新的外星人,暂停片刻,再接着往下执行。如果玩家没有了飞船,就将game_active
设置为False
。
七、确定应运行游戏的哪个部分
我们需要确定游戏的哪些部分在任何情况下都应运行,哪些部分仅在游戏处于活动状态时才运行:
在主循环中,在任何情况下都需要调用_check_events()
,即便游戏处于非活动状态。例如,我们需要知道玩家是否按了Q
键以退出游戏,或者是否单击了关闭窗口的按钮。我们还需要不断更新屏幕,以便在等待玩家是否选择开始新游戏时修改屏幕。其他的函数仅在游戏处于活动状态时才需要调用,因为游戏处于非活动状态时,不用更新游戏元素的位置。
现在运行这个游戏,它将在飞船用完后停止不动。
八、小结
在本节学习了:如何在游戏中添加大量相同的元素,如创建一群外星人;如何使用嵌套循环来创建元素网格,还通过调用每个元素的方法update()
移动了大量元素;如何控制对象在屏幕上移动方向,以及如何响应事件,如有外星人到达屏幕边缘;如何检测和响应子弹和外星人碰撞以及外星人和飞船碰撞;如何在游戏中跟踪统计信息,以及如何使用标志game_active
来判断游戏是否结束。
在与这个项目相关的最后一节中,我们将添加一个Play
按钮,让玩家能够开始游戏,以及在游戏结束后重玩。每当玩家消灭一群外星人后,我们都将加快游戏的节奏,并添加一个计分系统,得到一个极具可玩性的游戏!