14.1.1 创建 Button 类
由于Pygame没有内置创建按钮的方法,我们创建一个Button类,用于创建带标签的实心矩形。 你可以在游戏中使用这些代码来创建任何按钮。下面是Button类的第一部分,请将这个类保存为 文件button.py:
button.py
import pygame.font
class Button():
1 def __init__(self, ai_settings, screen, msg):"""初始化按钮的属性"""self.screen = screenself.screen_rect = screen.get_rect()# 设置按钮的尺寸和其他属性
2 self.width, self.height = 200, 50self.button_color = (0, 255, 0)self.text_color = (255, 255, 255)
3 self.font = pygame.font.SysFont(None, 48)# 创建按钮的rect对象,并使其居中
4 self.rect = pygame.Rect(0, 0, self.width, self.height)self.rect.center = self.screen_rect.center# 按钮的标签只需创建一次
5 self.prep_msg(msg)
首先,我们导入了模块pygame.font,它让Pygame能够将文本渲染到屏幕上。方法__init__() 接受参数self,对象ai_settings和screen,以及msg,其中msg是要在按钮中显示的文本(见1)。 我们设置按钮的尺寸(见2),然后通过设置button_color让按钮的rect对象为亮绿色,并通过设 置text_color让文本为白色。 在(见3)处,我们指定使用什么字体来渲染文本。实参None让Pygame使用默认字体,而48 指定了文本的字号。为让按钮在屏幕上居中,我们创建一个表示按钮的rect对象(见4),并将 其center属性设置为屏幕的center属性。
Pygame通过将你要显示的字符串渲染为图像来处理文本。在5处,我们调用prep_msg()来处 理这样的渲染。 prep_msg()的代码如下:
button.py
def prep_msg(self, msg):"""将msg渲染为图像,并使其在按钮上居中"""
1 self.msg_image = self.font.render(msg, True, self.text_color,self.button_color)
2 self.msg_image_rect = self.msg_image.get_rect()self.msg_image_rect.center = self.rect.center
方法prep_msg()接受实参self以及要渲染为图像的文本(msg)。调用font.render()将存储在 msg中的文本转换为图像,然后将该图像存储在msg_image中(见1)。方法font.render()还接受 一个布尔实参,该实参指定开启还是关闭反锯齿功能(反锯齿让文本的边缘更平滑)。余下的两 个实参分别是文本颜色和背景色。我们启用了反锯齿功能,并将文本的背景色设置为按钮的颜色 (如果没有指定背景色,Pygame将以透明背景的方式渲染文本)。
在2处,我们让文本图像在按钮上居中:根据文本图像创建一个rect,并将其center属性设 置为按钮的center属性。
最后,我们创建方法draw_button(),通过调用它可将这个按钮显示到屏幕上:
button.py
def draw_button(self):# 绘制一个用颜色填充的按钮,再绘制文本self.screen.fill(self.button_color, self.rect)self.screen.blit(self.msg_image, self.msg_image_rect)
我们调用screen.fill()来绘制表示按钮的矩形,再调用screen.blit(),并向它传递一幅图 像以及与该图像相关联的rect对象,从而在屏幕上绘制文本图像。至此,Button类便创建好了。
14.1.2 在屏幕上绘制按钮
我们将使用Button类来创建一个Play按钮。鉴于只需要一个Play按钮,我们直接在 alien_invasion.py中创建它,如下所示:
alien_invasion.py
--snip--
from game_stats import GameStats
from button import Button
--snip--
def run_game():--snip--pygame.display.set_caption("Alien Invasion")# 创建Play按钮
1 play_button = Button(ai_settings, screen, "Play")
--snip--# 开始游戏主循环while True:--snip--
2 gf.update_screen(ai_settings, screen, stats, ship, aliens, bullets,play_button)
run_game()
我们导入Button类,并创建一个名为play_button的实例(见1),然后我们将play_button传 递给update_screen(),以便能够在屏幕更新时显示按钮(见2)。
接下来,修改update_screen(),以便在游戏处于非活动状态时显示Play按钮:
game_functions.py
def update_screen(ai_settings, screen, stats, ship, aliens, bullets,play_button):"""更新屏幕上的图像,并切换到新屏幕"""--snip--# 如果游戏处于非活动状态,就绘制Play按钮if not stats.game_active:play_button.draw_button()# 让最近绘制的屏幕可见pygame.display.flip()
为让Play按钮位于其他所有屏幕元素上面,我们在绘制其他所有游戏元素后再绘制这个按 钮,然后切换到新屏幕。如果你现在运行这个游戏,将在屏幕中央看到一个Play按钮,如图14-1 所示。
14.1.3 开始游戏
为在玩家单击Play按钮时开始新游戏,需在game_functions.py中添加如下代码,以监视与这 个按钮相关的鼠标事件:
game_functions.py
def check_events(ai_settings, screen, stats, play_button, ship, bullets):"""响应按键和鼠标事件"""for event in pygame.event.get():if event.type == pygame.QUIT:--snip--
1 elif event.type == pygame.MOUSEBUTTONDOWN:
2 mouse_x, mouse_y = pygame.mouse.get_pos()
3 check_play_button(stats, play_button, mouse_x, mouse_y)
def check_play_button(stats, play_button, mouse_x, mouse_y):"""在玩家单击Play按钮时开始新游戏"""
4 if play_button.rect.collidepoint(mouse_x, mouse_y):stats.game_active = True
我们修改了check_events()的定义,在其中添加了形参stats和play_button。我们将使用stats 来访问标志game_active,并使用play_button来检查玩家是否单击了Play按钮。
无论玩家单击屏幕的什么地方,Pygame都将检测到一个MOUSEBUTTONDOWN事件(见1),但我 们只想让这个游戏在玩家用鼠标单击Play按钮时作出响应。为此,我们使用了pygame.mouse. get_pos(),它返回一个元组,其中包含玩家单击时鼠标的x和y坐标(见2)。我们将这些值传递 给函数check_play_button()(见3),而这个函数使用collidepoint()检查鼠标单击位置是否在 Play按钮的rect内(见4)。如果是这样的,我们就将game_active设置为True,让游戏就此开始! 在alien_invasion.py中调用check_events(),需要传递另外两个实参——stats和play_ button:
alien_invasion.py
# 开始游戏主循环while True:gf.check_events(ai_settings, screen, stats, play_button, ship,bullets)--snip--
至此,你应该能够开始这个游戏了。游戏结束时,game_active应为False,并重新显示Play 按钮。
14.1.4 重置游戏
前面编写的代码只处理了玩家第一次单击Play按钮的情况,而没有处理游戏结束的情况,因 为没有重置导致游戏结束的条件。
为在玩家每次单击Play按钮时都重置游戏,需要重置统计信息、删除现有的外星人和子弹、 创建一群新的外星人,并让飞船居中,如下所示:
game_functions.py
def check_play_button(ai_settings, screen, stats, play_button, ship, aliens,bullets, mouse_x, mouse_y):"""在玩家单击Play按钮时开始新游戏"""if play_button.rect.collidepoint(mouse_x, mouse_y):# 重置游戏统计信息
1 stats.reset_stats()stats.game_active = True# 清空外星人列表和子弹列表
2 aliens.empty()bullets.empty()# 创建一群新的外星人,并让飞船居中
3 create_fleet(ai_settings, screen, ship, aliens)ship.center_ship()
我们更新了check_play_button()的定义,使其能够访问ai_settings、stats、ship、aliens 和bullets。为重置在游戏期间发生了变化的设置以及刷新游戏的视觉元素,它需要这些对象。 在1处,我们重置了游戏统计信息,给玩家提供了三艘新飞船。接下来,我们将game_active 设置为True(这样,这个函数的代码执行完毕后,游戏就会开始),清空编组aliens和bullets(见 2),创建一群新的外星人,并将飞船居中(见3)。 check_events()的定义需要修改,调用check_play_button()的代码亦如此:
game_functions.py
def check_events(ai_settings, screen, stats, 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, play_button, ship,aliens, bullets, mouse_x, mouse_y)
check_events()的定义需要形参aliens,以便将它传递给check_play_button()。接下来,我 们修改了调用check_play_button()的代码,以将合适的实参传递给它(见1)。 下面来修改alien_invasion.py中调用check_events()的代码,以将实参aliens传递给它:
alien_invasion.py
# 开始游戏主循环while True: gf.check_events(ai_settings, screen, stats, play_button, ship,aliens, bullets)--snip--
现在,每当玩家单击Play按钮时,这个游戏都将正确地重置,让玩家想玩多少次就玩多少次!