我们将最高得分圆整到最近的10的整数倍(1),并添加了用逗号表示的千分位分隔符(见 2)。然后,我们根据最高得分生成一幅图像(见3),使其水平居中(见4),并将其top属性设 置为当前得分图像的top属性(见5)。
现在,方法show_score()需要在屏幕右上角显示当前得分,并在屏幕顶部中央显示最高得分:
scoreboard.py
def show_score(self): """在屏幕上显示当前得分和最高得分""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect)
为检查是否诞生了新的最高得分,我们在game_functions.py中添加一个新函数check_high_ score():
game_functions.py
函数check_high_score()包含两个形参:stats和sb。它使用stats来比较当前得分和最高得 分,并在必要时使用sb来修改最高得分图像。在处,我们比较当前得分和最高得分,如果当前 得分更高,就更新high_score的值,并调用prep_high_score()来更新包含最高得分的图像。 在check_bullet_alien_collisions()中,每当有外星人被消灭,都需要在更新得分后调用 check_high_score():
game_functions.py
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets): --snip-- if collisions: for aliens in collisions.values(): stats.score += ai_settings.alien_points * len(aliens) sb.prep_score() check_high_score(stats, sb) --snip--
字典collisions存在时,我们根据消灭了多少外星人来更新得分,再调用check_high_ score()。 第一次玩这款游戏时,当前得分就是最高得分,因此两个地方显示的都是当前得分。但再次 开始这个游戏时,最高得分出现在中央,而当前得分出现在右边,如图14-4所示。
14.3.8 显示等级
为在游戏中显示玩家的等级,首先需要在GameStats中添加一个表示当前等级的属性。为确 保每次开始新游戏时都重置等级,在reset_stats()中初始化它:
game_stats.py
def reset_stats(self): """初始化随游戏进行可能变化的统计信息""" self.ships_left = self.ai_settings.ship_limit self.score = 0 self.level = 1
为让Scoreboard能够在当前得分下方显示当前等级,我们在__init__()中调用了一个新方法 prep_level():
scoreboard.py
def __init__(self, ai_settings, screen, stats): --snip-- # 准备包含得分的初始图像 self.prep_score() self.prep_high_score() self.prep_level()
prep_level()的代码如下:
scoreboard.py
def prep_level(self): """将等级转换为渲染的图像""" 1 self.level_image = self.font.render(str(self.stats.level), True, self.text_color, self.ai_settings.bg_color) # 将等级放在得分下方 self.level_rect = self.level_image.get_rect() 2 self.level_rect.right = self.score_rect.right 3 self.level_rect.top = self.score_rect.bottom + 10
方法prep_level()根据存储在stats.level中的值创建一幅图像(见),并将其right属性设 置为得分的right属性(见)。然后,将top属性设置为比得分图像的bottom属性大10像素,以便 在得分和等级之间留出一定的空间(见)。 我们还需要更新show_score():
scoreboard.py
def show_score(self): """在屏幕上显示飞船和得分""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect)
在这个方法中,添加了一行在屏幕上显示等级图像的代码。 我们在check_bullet_alien_collisions()中提高等级,并更新等级图像: game_functions.py
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship, aliens, bullets): --snip-- if len(aliens) == 0: # 如果整群外星人都被消灭,就提高一个等级 bullets.empty() ai_settings.increase_speed() # 提高等级 1 stats.level += 1 2 sb.prep_level() create_fleet(ai_settings, screen, ship, aliens)
如果整群外星人都被消灭,我们就将stats.level的值加1(见),并调用prep_level(),以 确保正确地显示新等级(见)。 为确保开始新游戏时更新记分和等级图像,在按钮Play被单击时触发重置:
game_functions.py
def check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y): """在玩家单击Play按钮时开始新游戏""" button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y) if button_clicked and not stats.game_active: --snip-- # 重置游戏统计信息 stats.reset_stats() stats.game_active = True # 重置记分牌图像 1 sb.prep_score() sb.prep_high_score() sb.prep_level() # 清空外星人列表和子弹列表 aliens.empty() bullets.empty() --snip--
check_play_button()的定义需要包含对象sb。为重置记分牌图像,我们在重置相关游戏设置 后调用prep_score()、prep_high_score()和prep_level()(见)。 在check_events()中,现在需要向check_play_button()传递sb,让它能够访问记分牌对象:
game_functions.py
def check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: --snip-- elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x, mouse_y = pygame.mouse.get_pos() 1 check_play_button(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets, mouse_x, mouse_y)
check_events()的定义需要包含形参sb,这样调用check_play_button()时,才能将sb作为实 参传递给它(见)。 最后,更新alien_invasion.py中调用check_events()的代码,也向它传递sb:
alien_invasion.py
# 开始游戏主循环 while True: gf.check_events(ai_settings, screen, stats, sb, play_button, ship, aliens, bullets) --snip--
现在你可以知道升到多少级了,如图14-5所示。
注意
在一些经典游戏中,得分带标签,如Score、High Score和Level。我们没有显示这些标签, 因为开始玩这款游戏后,每个数字的含义将一目了然。要包含这些标签,只需在Scoreboard 中调用font.render()前,将它们添加到得分字符串中即可。
14.3.9 显示余下的飞船数
最后,我们来显示玩家还有多少艘飞船,但使用图形而不是数字。为此,我们在屏幕左上角 绘制飞船图像来指出还余下多少艘飞船,就像众多经典的街机游戏那样。 首先,需要让Ship继承Sprite,以便能够创建飞船编组:
ship.py
import pygame from pygame.sprite import Sprite 1 class Ship(Sprite): def __init__(self, ai_settings, screen): """初始化飞船,并设置其起始位置""" 2 super(Ship, self).__init__() --snip--
在这里,我们导入了Sprite,让Ship继承Sprite (见1),并在__init__()的开头就调用了super()(见2)。
接下来,需要修改Scoreboard,在其中创建一个可供显示的飞船编组。下面是其中的import 语句和方法__init__(): scoreboard.py
import pygame.font from pygame.sprite import Group from ship import Ship class Scoreboard(): """报告得分信息的类""" def __init__(self, ai_settings, screen, stats): --snip-- self.prep_level() self.prep_ships() --snip--
鉴于要创建一个飞船编组,我们导入Group和Ship类。调用prep_level()后,我们调用了 prep_ships()。 prep_ships()的代码如下:
scoreboard.py
def prep_ships(self): """显示还余下多少艘飞船""" 1 self.ships = Group() 2 for ship_number in range(self.stats.ships_left): ship = Ship(self.ai_settings, self.screen) 3 ship.rect.x = 10 + ship_number * ship.rect.width 4 ship.rect.y = 10 5 self.ships.add(ship)
方法prep_ships()创建一个空编组self.ships,用于存储飞船实例(见1)。为填充这个编组, 根据玩家还有多少艘飞船运行一个循环相应的次数(见2)。在这个循环中,我们创建一艘新飞 船,并设置其x坐标,让整个飞船编组都位于屏幕左边,且每艘飞船的左边距都为10像素(见3)。 我们还将y坐标设置为离屏幕上边缘10像素,让所有飞船都与得分图像对齐(见4)。最后,我们 将每艘新飞船都添加到编组ships中(见55)。 现在需要在屏幕上绘制飞船了:
scoreboard.py
def show_score(self): --snip-- self.screen.blit(self.level_image, self.level_rect) # 绘制飞船 self.ships.draw(self.screen)