添加播放按钮
在本节中,我们将添加一个 "播放" 按钮,该按钮会在游戏开始前出现,并在游戏结束时再次出现,以便玩家再次进行游戏。
现在,只要运行 alien_invasion.py,游戏就会开始。让我们以非活动状态启动游戏,然后提示玩家点击播放按钮开始游戏。为此,请修改 AlienInvasion
的 __init__()
方法:
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
--snip--
# Start Alien Invasion in an inactive state.
self.game_active = False
现在,游戏开始时应该处于非活动状态,玩家无法启动游戏,直到我们制作了 "播放" 按钮。
创建按钮类
因为 Pygame 没有制作按钮的内置方法,我们将编写一个 Button
类来创建一个带标签的填充矩形。您可以用这段代码来制作游戏中的任何按钮。下面是 Button
类的第一部分;将其保存为 button.py:
import pygame.font
class Button:
"""A class to build buttons for the game."""
def __init__(self, ai_game, msg):
"""Initialize button attributes."""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
# Set the dimensions and properties of the button.
self.width, self.height = 200, 50
self.button_color = (0, 135, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
# Build the button's rect object and center it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# The button message needs to be prepped only once.
self._prep_msg(msg)
首先,我们导入 pygame.font
模块,它可以让 Pygame 将文本呈现在屏幕上。__init__()
方法的参数是 self
、ai_game
对象和 msg
,其中 msg
包含按钮的文本 ❶。我们设置按钮的尺寸为 ❷,设置 button_color
将按钮的矩形对象染成深绿色,设置 text_color
将文本渲染为白色。
接下来,我们为渲染文本 ❸ 准备字体属性。None
参数告诉 Pygame 使用默认字体,48 指定文本的大小。为了使按钮在屏幕上居中,我们为按钮 ❹ 创建一个矩形,并将其 center
属性设置为与屏幕相匹配。
Pygame 处理文本时,会将要显示的字符串渲染为图像。最后,我们调用 _prep_msg()
来处理这种渲染❺。
下面是 _prep_msg()
的代码:
def _prep_msg(self, msg):
"""Turn msg into a rendered image and center text on the button."""
self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
_prep_msg()
方法需要一个 self
参数和要渲染为图像的文本 (msg
)。调用 font.render()
会将存储在 msg
中的文本转化为图像,然后将其存储在 self.msg_image
❶。font.render()
方法还包含一个布尔值,用于打开或关闭抗锯齿(抗锯齿会使文本边缘更平滑)。其余参数是指定的字体颜色和背景颜色。我们将抗锯齿设置为 True
,并将文本背景设置为与按钮相同的颜色。(如果不包含背景颜色,Pygame 将尝试以透明背景渲染字体)。
我们从图像中创建一个矩形,并将其中心属性设置为与按钮 ❷相匹配,从而使文本图像居中。
最后,我们创建了一个 draw_button()
方法,我们可以调用它在屏幕上显示按钮:
def draw_button(self):
"""Draw blank button and then draw message."""
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
我们调用 screen.fill()
绘制按钮的矩形部分。然后,我们调用 screen.blit()
将文本图像绘制到屏幕上,并将图像和与图像关联的矩形对象传给它。至此,按钮类就完成了。
将按钮绘制到屏幕上
我们将使用 Button
类在 AlienInvasion
中创建一个播放按钮。首先,我们将更新 import
语句:
--snip--
from game_stats import GameStats
from button import Button
因为我们只需要一个播放按钮,所以我们将在 AlienInvasion
的 __init__()
方法中创建该按钮。我们可以将这段代码放在 __init__()
的最后:
def __init__(self):
--snip--
self.game_active = False
# Make the Play button.
self.play_button = Button(self, "Play")
这段代码创建了一个标签为 Play 的 Button
实例,但并没有将按钮绘制到屏幕上。为此,我们将在 _update_screen()
中调用按钮的 draw_button()
方法:
def _update_screen(self):
--snip--
self.aliens.draw(self.screen)
# Draw the play button if the game is inactive.
if not self.game_active:
self.play_button.draw_button()
pygame.display.flip()
为了使 "播放" 按钮高于屏幕上的所有其他元素,我们在绘制完所有其他元素后,在翻转到新的屏幕前绘制该按钮。我们将其包含在一个 if
代码块中,因此只有当游戏处于非活动状态时,按钮才会出现。
现在,当你运行 外星入侵 时,应该会看到屏幕中央有一个播放按钮,如图 14-1 所示。

开始游戏
要在玩家单击 "播放" 时启动新游戏,请在 _check_events()
的末尾添加以下 elif
代码块,以监控按钮上的鼠标事件:
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip--
elif event.type == pygame.MOUSEBUTTONDOWN: (1)
mouse_pos = pygame.mouse.get_pos() (2)
self._check_play_button(mouse_pos) (3)
当玩家点击屏幕❶ 上的任意位置时,Pygame 会检测到一个 MOUSEBUTTONDOWN
事件,但我们希望限制我们的游戏只响应鼠标点击 Play 按钮。为了实现这一目标,我们使用了 pygame.mouse.get_pos()
,它会返回一个元组,其中包含点击鼠标按钮 ❷ 时鼠标光标的 x
坐标和 y
坐标。我们将这些值发送到新方法 _check_play_button()
❸。
下面是 _check_play_button()
,我选择将其放在 _check_events()
之后:
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
if self.play_button.rect.collidepoint(mouse_pos): (1)
self.game_active = True
我们使用矩形方法 collidepoint()
检查鼠标点击点是否与 Play 按钮的矩形❶ 所定义的区域重叠。如果是,我们就将 game_active
设置为 True
,游戏就开始了!
至此,你应该可以开始并玩一个完整的游戏了。当游戏结束时,game_active
的值应该变为 False
,而 Play 按钮应该重新出现。
重置游戏
我们刚刚编写的 "播放" 按钮代码在玩家第一次点击 "播放" 时起作用。但第一次游戏结束后就不起作用了,因为导致游戏结束的条件尚未重置。
为了在玩家每次点击 "播放" 时重置游戏,我们需要重置游戏统计信息,清除旧的外星人和子弹,建立新的舰队,并将飞船置于中心位置,如图所示:
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
if self.play_button.rect.collidepoint(mouse_pos):
# Reset the game statistics.
self.stats.reset_stats()
self.game_active = True
# Get rid of any remaining bullets and aliens.
self.bullets.empty()
self.aliens.empty()
# Create a new fleet and center the ship.
self._create_fleet()
self.ship.center_ship()
我们重置了游戏统计数据❶,这将为玩家提供三艘新战舰。然后我们将 game_active
设置为 True
,这样一旦该函数中的代码运行完毕,游戏就会开始。我们清空外星人和子弹群❷,然后创建一个新的舰队并将飞船置于中心位置❸。
现在,每次点击 "播放" 时,游戏都会正确重置,你可以尽情地玩游戏!
停用播放按钮
我们的 "播放" 按钮有一个问题,那就是即使 "播放" 按钮不可见,屏幕上的按钮区域也会继续响应点击。如果您在游戏开始后不小心点击了 "播放" 按钮区域,游戏就会重新启动!
要解决这个问题,可以将游戏设置为只有在 game_active
为 False
时才开始:
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos) (1)
if button_clicked and not self.game_active: (2)
# Reset the game statistics.
self.stats.reset_stats()
--snip--
button_clicked
标志存储的是 True
或 False
值❶,只有在点击 "播放" 且游戏当前未激活的情况下❷,游戏才会重新启动。要测试这一行为,请启动一个新游戏并重复点击 "播放" 按钮的位置。如果一切正常,点击 "播放" 按钮不会对游戏产生任何影响。
隐藏鼠标光标
我们希望鼠标光标在游戏未激活时是可见的,但一旦游戏开始,它就会挡住去路。为了解决这个问题,我们要让鼠标指针在游戏开始时不可见。我们可以在 _check_play_button()
中 if
代码块的末尾这样做:
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.game_active:
--snip--
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
向 set_visible()
传递 False
会告诉 Pygame 当鼠标在游戏窗口上时隐藏光标。
一旦游戏结束,我们将让光标重新出现,这样玩家就可以再次点击 Play 开始新游戏。下面是实现这一目的的代码:
def _ship_hit(self):
"""Respond to ship being hit by alien."""
if self.stats.ships_left > 0:
--snip--
else:
self.game_active = False
pygame.mouse.set_visible(True)
我们会在 _ship_hit()
中让光标在游戏停止后立即恢复可见。注意这样的细节能让您的游戏看起来更专业,让玩家专注于游戏,而不是琢磨用户界面。