14.3.2 创建记分牌
代码语言:javascript复制为显示得分,我们在alien_invasion.py中创建一个Scoreboard实例: alien_invasion.py
--snip--
from game_stats import GameStats
from scoreboard import Scoreboard
--snip--
def run_game():
--snip--
# 创建存储游戏统计信息的实例,并创建记分牌
stats = GameStats(ai_settings)
1 sb = Scoreboard(ai_settings, screen, stats)
--snip--
# 开始游戏主循环
while True:
--snip--
2 gf.update_screen(ai_settings, screen, stats, sb, ship, aliens,
bullets, play_button)
run_game()
代码语言:javascript复制我们导入新创建的类Scoreboard,并在创建实例stats后创建了一个名为sb的Scoreboard实例 (见1)。接下来,我们将sb传递给update_screen(),让它能够在屏幕上显示得分(见2)。 为显示得分,将update_screen()修改成下面这样: game_functions.py
def update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets,
play_button):
--snip--
# 显示得分
sb.show_score()
# 如果游戏处于非活动状态,就显示Play按钮
if not stats.game_active:
play_button.draw_button()
# 让最近绘制的屏幕可见
pygame.display.flip()
我们在update_screen()的形参列表中添加了sb,并在绘制Play按钮前调用show_score。 如果现在运行这个游戏,你将在屏幕右上角看到0(当前,我们只想在进一步开发记分系统 前确认得分出现在正确的地方)。图14-2显示了游戏开始前的得分。
下面来指定每个外星人值多少点!
14.3.3 在外星人被消灭时更新得分
代码语言:javascript复制为在屏幕上实时地显示得分,每当有外星人被击中时,我们都更新stats.score的值,再调 用prep_score()更新得分图像。但在此之前,我们需要指定玩家每击落一个外星人都将得到多少 个点: settings.py
def initialize_dynamic_settings(self):
--snip--
# 记分
self.alien_points = 50
代码语言:javascript复制随着游戏的进行,我们将提高每个外星人值的点数。为确保每次开始新游戏时这个值都会被 重置,我们在initialize_dynamic_settings()中设置它。 在check_bullet_alien_collisions()中,每当有外星人被击落时,都更新得分: game_functions.py
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets):
"""响应子弹和外星人发生碰撞"""
# 删除发生碰撞的子弹和外星人
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if collisions:
1 stats.score = ai_settings.alien_points
sb.prep_score()
--snip--
代码语言:javascript复制我们更新check_bullet_alien_collisions()的定义,在其中包含了形参stats和sb,让它能够 更新得分和记分牌。有子弹撞到外星人时,Pygame返回一个字典(collisions)。我们检查这个 字典是否存在,如果存在,就将得分加上一个外星人值的点数(见)。接下来,我们调用 prep_score()来创建一幅显示最新得分的新图像。 我们需要修改update_bullets(),确保在函数之间传递合适的实参: game_functions.py
def update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets):
"""更新子弹的位置,并删除已消失的子弹"""
--snip--
check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets)
代码语言:javascript复制在update_bullets()的定义中,需要新增形参stats和sb,而调用check_bullet_alien_collisions()时,也需要传递实参stats和sb。 我们还需要修改主while循环中调用update_bullets()的代码: alien_invasion.py
# 开始游戏主循环
while True:
gf.check_events(ai_settings, screen, stats, play_button, ship,
aliens, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens,
bullets)
--snip--
调用update_bullets()时,需要传递实参stats和sb。 如果你现在运行这个游戏,得分将不断增加!
14.3.4 将消灭的每个外星人的点数都计入得分
代码语言:javascript复制当前,我们的代码可能遗漏了一些被消灭的外星人。例如,如果在一次循环中有两颗子弹射 中了外星人,或者因子弹更宽而同时击中了多个外星人,玩家将只能得到一个被消灭的外星人的 点数。为修复这种问题,我们来调整检测子弹和外星人碰撞的方式。 在check_bullet_alien_collisions()中,与外星人碰撞的子弹都是字典collisions中的一个 键;而与每颗子弹相关的值都是一个列表,其中包含该子弹撞到的外星人。我们遍历字典 collisions,确保将消灭的每个外星人的点数都记入得分: game_functions.py
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets):
--snip--
if collisions:
1 for aliens in collisions.values():
stats.score = ai_settings.alien_points * len(aliens)
sb.prep_score()
--snip--
如果字典collisions存在,我们就遍历其中的所有值。别忘了,每个值都是一个列表,包含 被同一颗子弹击中的所有外星人。对于每个列表,都将一个外星人的点数乘以其中包含的外星人 数量,并将结果加入到当前得分中。为测试这一点,请将子弹宽度改为300像素,并核实你得到 了更宽的子弹击中的每个外星人的点数,再将子弹宽度恢复到正常值。
14.3.5 提高点数
代码语言:javascript复制玩家每提高一个等级,游戏都变得更难,因此处于较高的等级时,外星人的点数应更高。为实现这种功能,我们添加一些代码,以在游戏节奏加快时提高点数: settings.py
class Settings():
"""存储游戏《外星人入侵》的所有设置的类"""
def __init__(self):
--snip--
# 加快游戏节奏的速度
self.speedup_scale = 1.1
# 外星人点数的提高速度
1 self.score_scale = 1.5
self.initialize_dynamic_settings()
def increase_speed(self):
"""提高速度设置和外星人点数"""
self.ship_speed_factor *= self.speedup_scale
self.bullet_speed_factor *= self.speedup_scale
self.alien_speed_factor *= self.speedup_scale
2 self.alien_points = int(self.alien_points * self.score_scale)
代码语言:javascript复制我们定义了点数提高的速度,并称之为score_scale(见)。很小的节奏加快速度(1.1)让 游戏很快就变得极具挑战性,但为让记分发生显著的变化,需要将点数的提高速度设置为更大的 值(1.5)。现在,我们在加快游戏节奏的同时,提高了每个外星人的点数。为让点数为整数,我 们使用了函数int()。 为显示外星人的点数,我们在Settings的方法increase_speed()中添加了一条print语句: settings.py
def increase_speed(self):
--snip--
self.alien_points = int(self.alien_points * self.score_scale)
print(self.alien_points)
现在每当提高一个等级时,你都会在终端窗口看到新的点数值.
注意
确认点数在不断增加后,一定要删除这条print语句,否则它可能会影响游戏的性能以及 分散玩家的注意力。
14.3.6 将得分圆整
代码语言:javascript复制大多数街机风格的射击游戏都将得分显示为10的整数倍,下面让我们的记分系统遵循这个原 则。我们还将设置得分的格式,在大数字中添加用逗号表示的千位分隔符。我们在Scoreboard中执行这种修改: scoreboard.py
def prep_score(self):
"""将得分转换为渲染的图像"""
1 rounded_score = int(round(self.stats.score, -1))
2 score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color,
self.ai_settings.bg_color)
--snip--
函数round()通常让小数精确到小数点后多少位,其中小数位数是由第二个实参指定的。然 而,如果将第二个实参指定为负数,round()将圆整到最近的10、100、1000等整数倍。1处的代 码让Python将stats.score的值圆整到最近的10的整数倍,并将结果存储到rounded_score中。
注意
在Python 2.7中,round()总是返回一个小数值,因此我们使用int()来确保报告的得分为 整数。如果你使用的是Python 3,可省略对int()的调用。
2处使用了一个字符串格式设置指令,它让Python将数值转换为字符串时在其中插入逗号, 例如,输出1,000,000而不是1000000。如果你现在运行这个游戏,看到的将是10的整数倍的整洁 得分,即便得分很高亦如此,如图14-3所示。
14.3.7 最高得分
代码语言:javascript复制每个玩家都想超过游戏的最高得分记录。下面来跟踪并显示最高得分,给玩家提供要超越的 目标。我们将最高得分存储在GameStats中: game_stats.py
def __init__(self, ai_settings):
--snip--
# 在任何情况下都不应重置最高得分
self.high_score = 0
代码语言:javascript复制鉴于在任何情况下都不会重置最高得分,我们在__init__()中而不是reset_stats()中初始化 high_score。 下面来修改Scoreboard以显示最高得分。先来修改方法__init__(): scoreboard.py
def __init__(self, ai_settings, screen, stats):
--snip--
# 准备包含最高得分和当前得分的图像
self.prep_score()
1 self.prep_high_score()
代码语言:javascript复制最高得分将与当前得分分开显示,因此我们需要编写一个新方法prep_high_score(),用于准 备包含最高得分的图像(见)。 方法prep_high_score()的代码如下: scoreboard.py
def prep_high_score(self):
"""将最高得分转换为渲染的图像"""
1 high_score = int(round(self.stats.high_score, -1))
2 high_score_str = "{:,}".format(high_score)
3 self.high_score_image = self.font.render(high_score_str, True,
self.text_color, self.ai_settings.bg_color)
#将最高得分放在屏幕顶部中央
self.high_score_rect = self.high_score_image.get_rect()
4 self.high_score_rect.centerx = self.screen_rect.centerx
5 self.high_score_rect.top = self.score_rect.top