在这篇博文里,我们将为我方飞机添加三条生命。
1、加载相关资源
在目前的游戏中,我方飞机是拥有不死生命的,损毁了就在下方复活,这显然不合逻辑,因此需要为我放飞机增加生命数,并在屏幕右下方显示对应图标。首先在main()函数中设置初始化生命数并加载小飞机图片:
~~~
life_image = pygame.image.load("image/life.png").convert()
life_rect = life_image.get_rect()
life_num = 3 # 一共有三条命
~~~
2、将“life_num”加入程序循环判断语句中
既然我方飞机拥有了生命属性,那就意味着我们不能够再向之前那样随意的摆放while()循环中的代码了,很多代码在执行前都需要判断life_num是否为零,比如说发射子弹,我方飞机都已经挂掉了,再发射子弹就显得不尽合理了吧。
因此我们需要考虑哪些代码在执行前需要判断life_num是否为零,具体来说,只要涉及到和精灵有关的操作都应该在life_num > 0时进行,因此我们将下列操作放在life_num的判断条件之外:(1)打印背景图片和分数的操作。因为不能因为我方飞机挂掉就让屏幕一篇漆黑,背景还是应该有的;(2)事件响应机制。因为我方飞机挂掉之后同样需要响应用户事件,比如鼠标单击事件、系统退出事件。除了以上这两个操作之外,其余操作都应该放在“if life_num:”的条件循环之内,因此可能需要我们将之前的代码顺序做一些调整:
~~~
while running:
# 将背景图片打印到内存的屏幕上# 检测用户的退出及暂停操作
~~~
~~~
if life_num
# 绘制全屏炸弹数量和剩余生命数量# 检测用户的键盘操作# 绘制补给并检测玩家是否获得# 子弹与敌机的碰撞检测
# 我方飞机碰撞检测
# 绘制我方飞机# 绘制敌方飞机
# …………………………
~~~
~~~
elif life_num == 0: # 生命值为零,绘制游戏结束画面
pass
# 将内存中绘制好的屏幕刷新到设备屏幕上
# 设置帧数为60
~~~
3、life_num递减操作
重新安排完代码结构后,接下来只需在我方飞机损毁时,将life_num减一:
~~~
if me.active:# 绘制我方飞机的两种不同的形式else:
if not (delay % 3):
# 绘制我方飞机损毁画面
if me_destroy_index == 0:
life_num -= 1# 我方飞机重生
~~~
当然这里也用了“me_destroy_index == 0”的限定条件,以保证在损毁期间只减一次生命数(详见之前博文)。
4、绘制life_num图标
在游戏过程中,为了显示当前玩家还有多少生命数,需要通过在屏幕右下方显示小飞机图标来指示,玩家当前有几条命就显示几个图标。小图标的资源已将在开始时加载好了,我们先给出完整的显示代码,稍后解释:
~~~
for i in range(life_num):
screen.blit(life_image, (width - 10 - (i + 1) * life_rect.width, height - 10 - life_rect.height))
~~~
这里只是做了一些简单的数学运算,“(width - 10 - (i + 1) * life_rect.width, height - 10 - life_rect.height)”这段代码就能够实现根据当前的life_num的值画出对应数量的小飞机图标。
5、为我方飞机添加初始化无敌机制
程序编写到这里貌似已经实现了预期目标,不过其实这里面有一个影藏的BUG,需要多次实现才会出现。因为我方飞机在重生时都是在固定位置出现的,那么就有这样一种可能性:就是在我方飞机重生时,在屏幕下方正中间(我方飞机重生的位置)正好有一架敌机,这就导致我方飞机一出生就挂掉,根本没有躲避的事件,虽然这种BUG出现的几率不大,但我们还是在这里提供一个解决方案,就是让我方飞机在重生之后具有三秒的无敌时间。因此我方飞机又多了一个属性:无敌。
首先向我方飞机类的内部添加用来表示当前无敌状态的标志位:
~~~
self.invincible = False # 飞机初始化时有三秒的无敌时间
~~~
对应的在reset()成员函数中将其置为true:
~~~
self.invincible = True
~~~
接下来我们通过事件触发系统来设计一个定时器,用以记录无敌时间,首先在main函数中定义这个计时器:
~~~
invincible_time = USEREVENT + 2 # 接触我方飞机无敌时间定时器
~~~
由于之前已经定义了两个用户事件了(supply_timer,double_bullet_timer),因此这里指定的标号为USEREVENT + 2。
然后在我方飞机reset之后(invincible置为true,无敌状态开始),激活这个定时器,计时开始(3秒):
~~~
if me.active:# 绘制我方飞机的两种不同的形式else:
if not (delay % 3):
# 绘制我方飞机损毁画面
if me_destroy_index == 0:
me.reset() # 我方飞机重生并开始无敌时间计时
pygame.time.set_timer(invincible_time, 3 * 1000)
~~~
激活定时器之后,编写事件响应函数。若该事件被触发,说明三秒的无敌时间已过,需要将我方飞机的无敌标志位置为false,并关闭计时器:
~~~
for event in pygame.event.get(): # 响应用户的偶然操作
if event.type == QUIT:
# 如果用户按下屏幕上的关闭按钮,触发QUIT事件,程序退出elif event.type == invincible_time: # 如果无敌时间已过
me.invincible = False
pygame.time.set_timer(invincible_time, 0)
~~~
最后,我们修改一下我方飞机的碰撞检测函数,使得当我方飞机与敌机发生碰撞时,只有在我方飞机的invincible变量为false(非无敌状态)的情况下,才执行我方飞机和敌机的销毁操作:
~~~
# ====================我方飞机碰撞检测====================
enemies_down = pygame.sprite.spritecollide(me, enemies, False, pygame.sprite.collide_mask)
if enemies_down and not me.invincible: # 如果碰撞检测返回的列表非空,则说明已发生碰撞,若此时我方飞机处于无敌状态
me.active = False
for e in enemies_down:
e.active = False # 敌机损毁
~~~
这里就是在原有的if语句调价的基础上添加“not me.invincible”判断,且二者为“与”操作的关系。好的,程序到此就告一段落,顺利的实现了预期功能,在下一篇博文中我们在给游戏添加一个暂停的功能。