摘要:之所以這里要添加這四行代碼,其實是為了當你重新開始也就是第二次及以后點擊按鈕游戲時,計分板能正確顯示。當第一運行游戲時,沒有這四行也能正確顯示計分板。
《Python編程:從入門到實踐》筆記。1. 前言
本篇是Python小游戲《外星人入侵》的最后一篇。
本篇我們將結束Pygame小游戲《外星人入侵》的開發。在本篇中,我們將添加如下內容:
添加一個Play按鈕,用于根據需要啟動游戲以及在游戲結束后重啟游戲;
使玩家能提高等級,并在提高等級時加快節奏;
添加一個記分系統
2. 添加Play按鈕首先為了通過點擊Play按鈕來開始游戲,需要在GameStats類的構造函數中將self.game_active設置為False。
2.1 Button類為了添加Play按鈕,我們需要先添加一個Button類。將這個類放在button.py模塊中:
import pygame class Button: def __init__(self, ai_settings, screen, msg): """初始化按鈕屬性""" self.screen = screen self.screen_rect = screen.get_rect() # 設置按鈕尺寸和其他屬性 self.width, self.height = 200, 50 # 解包,平行賦值 self.button_color = (0, 255, 0) self.text_color = (255, 255, 255) self.font = pygame.font.SysFont(None, 48) # 創建按鈕的rect對象,并使其居中 self.rect = pygame.Rect(0, 0, self.width, self.height) self.rect.center = self.screen_rect.center # 按鈕的標簽只需創建一次 self.prep_msg(msg) def prep_msg(self, msg): """將msg渲染為圖像,并使其在按鈕上居中""" 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 def draw_button(self): # 繪制一個用顏色填充的按鈕,再繪制文本 self.screen.fill(self.button_color, self.rect) self.screen.blit(self.msg_image, self.msg_image_rect)
pygame將字符串渲染為圖像來處理文本,通過pygame.font的render()方法來渲染文字,它的第一個參數是要渲染的字符串,第二個是抗鋸齒設定(打游戲的老鐵應該對這個詞很熟悉~~),第三個是字體顏色,第四個是背景顏色,第四個參數如果不設定,將以透明背景的方式渲染文本。最后通過draw_button()方法在窗體中繪制Play按鈕。
2.2 修改alien_invasion.py在主程序中實例化一個Play按鈕,并添加它的響應事件,以及將其畫出。
-- snip -- from button import Button -- snip -- def run_game(): -- snip -- pygame.display.set_caption("Alien Invasion") # 創建Play按鈕 play_button = Button(ai_settings, screen, "Play") -- snip -- # 開始游戲的主循環 while True: # 增加了參數,為按鈕添加響應事件 gf.check_events(ai_settings, screen, ship, bullets, stats, play_button, aliens) -- snip -- # 增加了參數,在窗體中畫出按鈕 gf.update_screen(ai_settings, screen, ship, bullets, aliens, stats, play_button) run_game()
注意,不光新增了實例化按鈕的代碼,還修改了update_screen()和check_events()函數。
2.3 修改game_functions.py修改update_screen()函數:在窗體中畫出Play按鈕
# 增加了參數,記得修改主程序 def update_screen(ai_settings, screen, ship, bullets, aliens, stats, play_button): -- snip -- # 如果游戲沒啟動,則顯示Play按鈕 if not stats.game_active: play_button.draw_button() # 讓最近繪制的屏幕可見 pygame.display.flip()
修改check_events()函數:為Play按鈕添加響應事件
# 增加了參數,記得修改主程序 def check_events(ai_settings, screen, ship, bullets, stats, play_button, aliens): for event in pygame.event.get(): -- snip -- elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x, mouse_y = pygame.mouse.get_pos() check_play_button(stats, play_button, mouse_x, mouse_y, ai_settings, screen, ship, aliens, bullets)
pygame.MOUSEBUTTONDOWN表示鼠標按下事件;通過pygame.mouse的get_pos()來獲得鼠標點擊處的坐標;最后,通過check_play_button()函數來響應鼠標點擊事件,該函數的內容如下:
新增check_play_button()函數:處理鼠標點擊事件
def check_play_button(stats, play_button, mouse_x, mouse_y, ai_settings, screen, ship, aliens, bullets): """在玩家單機Play按鈕時開始新游戲""" if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active: # 隱藏光標 pygame.mouse.set_visible(False) # 重置游戲統計信息 stats.reset_stats() stats.game_active = True # 清空外星人列表和子彈列表 aliens.empty() bullets.empty() # 創建一群新的外星人,并讓飛船居中 create_fleet(ai_settings, screen, ship, aliens) ship.center_ship()
通過play_button.rect的collidepoint()方法來確定鼠標是否點擊到了button,如果點擊到了,并且當前游戲是“非啟動”狀態,則啟動或者重置游戲;
如果不對stats.game_active進行確認,則在游戲中,即使Play按鈕消失了,鼠標點擊它原來所在的地方,也會重置游戲。
在游戲中,為了避免光標的影響,游戲時我們通過pygame.mouse的set_visible()方法將其隱藏;游戲結束時,重新顯示光標,為此,需要修改ship_hit()函數:
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets): -- snip -- else: -- snip -- pygame.mouse.set_visible(True)
最后,程序的效果如下:
3. 游戲提速每當消滅一批艦隊后,我們就為游戲里的元素提個速,為此,需要修改settings.py和game_functions.py模塊。
3.1 修改settings.py添加一個提速倍率參數,并增加兩個方法:
class Settings: def __init__(self): -- snip -- # 以什么樣的速度提節奏 self.speedup_scale = 1.1 # 前面有四個屬性放到了該方法中 self.initialize_dynamic_settings() def initialize_dynamic_settings(self): """初始化隨游戲進行而變化的設置""" self.ship_speed_factor = 1.5 self.bullet_speed_factor = 3 self.alien_speed_factor = 1 # 外星艦隊方向標志:1向右,-1向左 self.fleet_direction = 1 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
我們將需要修改的四個參數放到了initialize_dynamic_settings()方法中,increase_speed()方法用于動態改變游戲參數。
3.2 修改game_functions.py每消滅一批外星艦隊,就對游戲提速,需要修改check_bullet_alien_collisions()函數:
def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets): -- snip -- if len(aliens) == 0: -- snip -- ai_settings.increase_speed() -- snip --
當重新開始游戲時,需要將這些被修改了的參數改回默認值,為此,需要修改check_play_button()函數:
def check_play_button(stats, play_button, mouse_x, mouse_y, ai_settings, screen, ship, aliens, bullets): if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active: # 重置游戲設置 ai_settings.initialize_dynamic_settings() -- snip --4. 記分板
下面我們將實現一個記分系統,實時跟蹤玩家的得分,并顯示最高得分,當前等級和余下的飛船數。首先,我們需要創建一個Scoreboard類。
4.1 新增scoreboard.py新增一個Scoreboard類,用作屏幕中的記分板,它的屏幕正中央上方部分是最高分數,屏幕右邊是當前分數和等級,左上角是剩余的飛船數量,飛船數量用圖片表示,因此,我們還要將Ship類更改為從Sprite繼承。
import pygame from pygame.sprite import Group from ship import Ship class Scoreboard: """顯示得分信息的類""" def __init__(self, ai_settings, screen, stats): """初始化顯示得分涉及的屬性""" self.screen = screen self.screen_rect = screen.get_rect() self.ai_settings = ai_settings self.stats = stats # 顯示得分信息時使用的字體設置 self.text_color = (30, 30, 30) self.font = pygame.font.SysFont(None, 48) # 生成當前得分、最高得分、當前等級和當前剩余的飛船數 self.prep_score() self.prep_high_score() self.prep_level() self.prep_ships() def prep_score(self): """將得分轉換為圖片""" rounded_score = round(self.stats.score, -1) # 在得分中插入逗號 score_str = "{:,}".format(rounded_score) self.score_image = self.font.render(score_str, True, self.text_color, self.ai_settings.bg_color) # 將得分放在屏幕右上角 self.score_rect = self.score_image.get_rect() self.score_rect.right = self.screen_rect.right - 20 self.score_rect.top = 20 def prep_high_score(self): """將最高得分轉化為圖像""" high_score = round(self.stats.high_score, -1) high_score_str = "{:,}".format(high_score) 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() self.high_score_rect.centerx = self.screen_rect.centerx self.high_score_rect.top = self.score_rect.top def prep_level(self): """將等級轉化為圖像""" 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() self.level_rect.right = self.score_rect.right self.level_rect.top = self.score_rect.bottom + 10 def prep_ships(self): """顯示還余下多少艘飛船""" self.ships = Group() for ship_number in range(self.stats.ships_left): ship = Ship(self.ai_settings, self.screen) ship.rect.x = 10 + ship_number * ship.rect.width ship.rect.y = 10 self.ships.add(ship) 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) # 繪制飛船 self.ships.draw(self.screen)4.2 修改settings.py
設置外星人的分數,外星人分數增長的速度:
class Settings: def __init__(self): -- snip -- # 外星人點數的提高速度 self.score_scale = 1.5 self.initialize_dynamic_settings() def initialize_dynamic_settings(self): -- snip -- # 記分, 每一個外星人的分數 self.alien_points = 50 def increase_speed(self): -- snip -- # 動態增加每個外星人的分數 self.alien_points = int(self.alien_points * self.score_scale)4.3 修改game_stats.py
在GameStats中設置一個用于記錄最高分的屬性,也正因此,應該將它放在構造函數中,它只會變大,在沒有重新運行游戲前,它不會被重置為0;在reset_stats()方法中,初始化score和level兩個屬性,這兩個屬性每點一次Play按鈕都會被重置。對于level這個屬性,每消滅一批艦隊,level就加1.
class GameStats: def __init__(self, ai_settings): -- snip -- # 在任何情況下都不應重置最高得分 self.high_score = 0 def reset_stats(self): -- snip -- self.score = 0 self.level = 14.4 修改主程序alien_invasion.py
-- snip -- from scoreboard import Scoreboard def run_game(): -- snip -- # 創建計分板 score = Scoreboard(ai_settings, screen, stats) # 開始游戲的主循環 while True: # 添加score參數 gf.check_events(ai_settings, screen, ship, bullets, stats, play_button, aliens, score) if stats.game_active: ship.update() # 添加score參數 gf.update_bullets(bullets, aliens, ship, screen, ai_settings, stats, score) # 添加score參數 gf.update_aliens(ai_settings, aliens, ship, screen, bullets, stats, score) # 添加score參數 gf.update_screen(ai_settings, screen, ship, bullets, aliens, stats, play_button, score)
從上面的注釋可以看出,我們生成了一個計分板的實例score;game_functions.py中的四個函數都要添加score參數,換句話說,這四個函數都要修改,下面我們逐一修改這四個函數。
4.5 修改game_functions.py 4.5.1 修改參數有幾個函數只需要在參數列表中增加score參數:
# 增加score參數 def check_events(ai_settings, screen, ship, bullets, stats, play_button, aliens, score): for event in pygame.event.get(): -- snip -- elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x, mouse_y = pygame.mouse.get_pos() # 增加score參數, 該函數有所改動 check_play_button(stats, play_button, mouse_x, mouse_y, ai_settings, screen, ship, aliens, bullets, score) # 增加score參數 def update_bullets(bullets, aliens, ship, screen, ai_settings, stats, score): -- snip -- # 增加score參數,該函數有所改動 check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets, stats, score) # 增加score參數 def update_aliens(ai_settings, aliens, ship, screen, bullets, stats, score): -- snip -- if pygame.sprite.spritecollideany(ship, aliens): # 增加score參數,該函數有所改動 ship_hit(ai_settings, stats, screen, ship, aliens, bullets, score) # 增加score參數,該函數有所改動 check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets, score) # 增加score參數,該函數有所改動 def update_screen(ai_settings, screen, ship, bullets, aliens, stats, play_button, score): -- snip -- aliens.draw(screen) # 在if語句前面添加繪制計分板的代碼 # 顯示得分 score.show_score() if not stats.game_active: play_button.draw_button() -- snip --
接下來是改動較多的函數。
4.5.2 修改check_play_button()函數# 添加了score參數 def check_play_button(stats, play_button, mouse_x, mouse_y, ai_settings, screen, ship, aliens, bullets, score): """在玩家單機Play按鈕時開始新游戲""" if play_button.rect.collidepoint(mouse_x, mouse_y) and not stats.game_active: -- snip -- stats.game_active = True # 這一句不是新增的 # 以下四行是新增的 score.prep_score() score.prep_high_score() score.prep_level() score.prep_ships() # 清空外星人列表和子彈列表 -- snip --
首先參數列表添加了score參數,if判斷中還添加了四行生成計分板的代碼。之所以這里要添加這四行代碼,其實是為了當你重新開始(也就是第二次及以后點擊Play按鈕)游戲時,計分板能正確顯示。
當第一運行游戲時,沒有這四行也能正確顯示計分板。但是從第二次點擊Play開始,如果沒有這四行,游戲的各個參數雖然更新了(通過check_play_button()中的各種重置函數得到了更新),可這些更新還沒有讓記分板中這四個參數的圖像得到重新繪制,即屬性的更新沒有自動觸發score的這四個函數。所以顯示會不正確,因此必須在這里添加這四行代碼。
4.5.3 修改update_screen()函數# 增加了score參數 def update_screen(ai_settings, screen, ship, bullets, aliens, stats, play_button, score): -- snip -- # 增加顯示得分的代碼 score.show_score() if not stats.game_active: -- snip --4.5.4 修改update_bullets()和update_aliens()函數
這倆函數只是增加參數而已。
# 增加了score參數 def update_bullets(bullets, aliens, ship, screen, ai_settings, stats, score): -- snip -- # 增加了score參數, 函數有改動 check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets, stats, score) # 增加了score參數 def update_aliens(ai_settings, aliens, ship, screen, bullets, stats, score): -- snip -- # 檢測外星人和飛船之間的碰撞 if pygame.sprite.spritecollideany(ship, aliens): # 增加了score參數, 函數有改動 ship_hit(ai_settings, stats, screen, ship, aliens, bullets, score) # 增加了score參數 check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets, score)
check_aliens_bottom()內也變化也不大,該函數的變化不再以代碼的形式多帶帶列出:
該函數增加了一個score參數,它內部調用了ship_hit()函數,為這個調用也增加score參數。這就是全部變化。
4.5.5 修改check_bullet_alien_collisions()函數# 增加了score參數 def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets, stats, score): collisions = pygame.sprite.groupcollide(bullets, aliens, True, True) if collisions: for aliens in collisions.values(): stats.score += ai_settings.alien_points * len(aliens) # 其實這里可以將其放到for循環之外,應為并不能立刻就呈現分數變化 # 要等到主程序中的update_screen()中才能呈現 score.prep_score() # 該函數是新增的 check_high_score(stats, score) if len(aliens) == 0: # 刪除現有的子彈并創建新的艦隊 bullets.empty() ai_settings.increase_speed() # 提高等級 stats.level += 1 score.prep_level() create_fleet(ai_settings, screen, ship, aliens)
首先我們增加了一個判斷語句,用于根據消滅的外星人來增加分數,由于有可能一顆子彈打到多個外星人但只算了一個外星人的分數,所有用循環來確保消滅掉的每一個外星人都得到了統計。collisions是一個字典,這里子彈是鍵,該子彈消滅的外星人對象為值(是個列表)。
我們還新增了一個更新最高積分的函數check_high_score(),它的代碼如下:
def check_high_score(stats, score): """檢查是否誕生了新的最高得分""" if stats.score > stats.high_score: stats.high_score = stats.score score.prep_high_score()
第二個if中,添加了增加等級的語句,緊跟著的是重新在計分板中繪制等級圖像。
4.5.6 修改ship_hit()和check_aliens_bottom()函數# 增加了score參數 def ship_hit(ai_settings, stats, screen, ship, aliens, bullets, score): if stats.ships_left > 0: stats.ships_left -= 1 # 更新記分牌 score.prep_ships() # 清空外星人列表和子彈列表 -- snip --4.6 最后運行效果
至此所有的添加都已經結束,下圖是游戲的最終效果:
5. 小結Python小游戲告一段落,一共三篇文章。本文中講述了:
如何創建用于開始新游戲的Play按鈕;
如何檢測鼠標點擊事件;
如何在游戲處于活動狀態時隱藏光標;
如何隨游戲的進行調整節奏;
如何實現記分系統;
以及如何以文本和非文本方式顯示信息。
后三篇文章將是使用Python來進行數據統計分析、繪圖等內容。
迎大家關注我的微信公眾號"代碼港" & 個人網站 www.vpointer.net ~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41788.html
摘要:如果你的目標是成為數據科學家或機器學習工程師研究員,那么有博士學位會給你加分不少。當然,有些人更喜歡學術研究,而不是在行業中運用數據科學或機器學習。二碩士學位入行數據科學需要碩士學位嗎視情況而定。 showImg(https://segmentfault.com/img/bVbm5Mw?w=850&h=566);作者 | Jeremie Harris翻譯 | MikaCDA 數據分析師...
摘要:商業軟件聯盟周三稱,一些歐洲國家和快速增長的發展中國家如巴西和中國擔心美國將輕易獲取新興的云計算市場。因此,這些國家建立了一些屏障阻止數據跨越其國境,從而阻礙了全球云計算市場的發展。 商業軟件聯盟周三稱,一些歐洲國家和快速增長的發展中國家(如巴西和中國)擔心美國將輕易獲取新興的云計算市場。因此,這些國家建立了一些屏障阻止數據跨越其國境,從而阻礙了全球云計算市場的發展。 ? 商業軟件聯盟CE...
摘要:好啦,首先讓我們先搞明白基礎定義,到底是什么表達了中用于創建匿名函數的特殊語法。其實總結起來,可以理解為一個小的匿名函數,函數可以使用任意數量的參數,但只能有一個表達式。 lambda是什么 大家好,今天給大家帶來的是有關于Python里面的lambda表達式詳細解析。lambda在Python里面的用處很廣,但說實話,我個人認為有關于lambda的討論不是如何使用的問題,而是該不該用...
摘要:所謂遞歸其實就是函數本身調用函數,直到滿足指定條件之后一層層退出函數,例如從前有座山,山里有座廟,廟里有個老和尚,正在給小和尚講故事呢故事是什么呢從前有座山,山里有座廟,廟里有個老和尚,正在給小和尚講故事呢故事是什么呢從前有座山,山里有座廟 所謂遞歸其實就是函數本身調用函數,直到滿足指定條件之后一層層退出函數, 例如 從前有座山,山里有座廟,廟里有個老和尚,正在給小和尚講故事呢!故事是...
閱讀 2009·2021-11-24 09:39
閱讀 1878·2019-08-30 15:55
閱讀 2168·2019-08-30 15:53
閱讀 565·2019-08-29 13:16
閱讀 984·2019-08-26 12:20
閱讀 2379·2019-08-26 11:58
閱讀 3129·2019-08-26 10:19
閱讀 3296·2019-08-23 18:31