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按鈕時,這個游戲都將正確地重置,讓玩家想玩多少次就玩多少次!