日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于pygame实现的飞机大战游戏

發布時間:2024/9/20 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于pygame实现的飞机大战游戏 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 1.引言
    • 1.1 背景
    • 1.2 意義
    • 1.3 功能
  • 2.系統結構
    • 2.1 整體框架
    • 2.2 精靈與精靈組
    • 2.3 功能介紹
      • 2.3.1 玩家飛機
      • 2.3.2 敵機類型和關卡設定
      • 2.3.3 敵機登場
      • 2.3.4 游戲道具和獎勵
      • 2.3.5 游戲結束
    • 2.4 游戲模塊
  • 3.實現過程及代碼
    • 3.1 游戲框架搭建
      • 3.1.1 游戲類的設計
      • 3.1.2 搭建游戲框架
    • 3.2 精靈與精靈組
      • 3.2.1 圖像的加載和繪制
      • 3.2.2 游戲背景連續滾動
    • 3.3 指示器面板
      • 3.3.1 指示器面板類的設計
      • 3.3.3 使用精靈實現文本標簽
      • 3.3.4 游戲數據修改
      • 3.3.5 最好成績的文件讀寫
      • 3.3.6 游戲狀態變化
      • 3.3.7 游戲結束后的重置面板
    • 3.4 逐幀動畫的基本實現
      • 3.4.1 派生飛機類
      • 3.4.2 設置逐幀動畫頻率
      • 3.5.1 派生敵機類
      • 3.5.2 派生英雄飛機類
    • 3.6 碰撞檢測
      • 3.6.1 創建碰撞檢測
      • 3.6.2 敵機撞毀英雄飛機
      • 3.6.3 英雄飛機發射子彈
      • 3.6.4 英雄飛機拾取道具
    • 3.7 音樂與音效
      • 3.7.1 加載和播放背景音樂
      • 3.7.1 加載和播放音效
    • 4.實驗結果
    • 5.總結和展望

?

**摘要**


??為了深入學習python程序設計語言面向對象設計的特點,以此開發一款桌面版的飛機大戰游戲,學習python程序設計語言中數據與功能的組合。此項目是基本pygame開發的一款桌面版的飛機大戰游戲。通過pygame中的精靈與精靈組的特性實現游戲面板上數據內容和圖像內容的顯示,實現飛機、子彈、道具與敵機的生成。幀數的設置實現游戲頁面的顯示和飛機的動態效果,監聽游戲事件以實現游戲事件的執行,pygame中的collide模塊實現飛機、子彈、道具和敵機之間的碰撞接觸,mixer模塊實現背景音樂和游戲音效。游戲有良好的界面顯示,簡單的游戲操作,給玩家良好的游戲體驗。 ?

1.引言

1.1 背景

?此項目是基于pygame開發的一款飛機大戰游戲,良好的游戲界面,玩家通過簡單的鍵盤操作即可進行游戲,體驗游戲樂趣。

1.2 意義

?以pygame開發飛機大戰游戲,體會和理解面向對象開發的同時,了解并學習pygame的高級使用

1.3 功能

?(1)顯示游戲背景圖片
?(2)顯示游戲狀態、游戲分數、炸彈提示、生命次數和文本提示等與游戲數據或狀態相關的內容
?(3)顯示玩家飛機與敵機的逐幀動畫,根據游戲關卡隨機生成相應數量的敵機
?(4)玩家飛機每0.2秒自動連續發射3顆子彈,子彈沿屏幕上方飛行,若與敵機接觸則碰撞擊毀敵機
?(5)玩家飛機通過方向鍵進行移動,還可以通過按鍵“B”釋放炸彈,每次釋放炸彈后,數量減一,當數量為0時,則不可使用
?(6)每30秒隨機從屏幕頂部掉落道具,玩家飛機拾取道具可增加炸彈或增強子彈
?(7)玩家飛機若與敵機碰撞,則減少生命次數,當生命次數達到0則游戲結束
?(8)播放音樂與音效

2.系統結構

2.1 整體框架

圖1 整體框架

?

2.2 精靈與精靈組

為了實現高仿真的飛機大戰游戲,使用了兩個由pygam提供的非常重要的類:精靈(Sprite)和精靈組(Group)
(1)精靈:
?A.在游戲開發中,通常把顯示圖像的對象叫做精靈Sprite
?B.精靈需要兩個重要的屬性:
??a. image 要顯示的圖像
??b. rect 圖像要顯示在屏幕的位置
?C.子類可以重寫update()方法,在每次刷新屏幕時,更新精靈位置,需 在子類的初始化方法中,設置image和rect屬性
(2)精靈組:
?A.一個精靈組可以包含多個精靈對象
?B. 調用精靈組對象的update()方法,可以自動調用組內每一個精靈的 update()方法
?C. 調用精靈組對象的draw()方法,可以將組內每一個精靈的image繪制在rect位置

2.3 功能介紹

2.3.1 玩家飛機

(1)在飛機大戰游戲中,玩家可以操作玩家飛機(又叫英雄飛機)執行移動位置、引爆炸彈以及拾取道具等動作,英雄飛機還能夠自動向上連續發射子彈,其中:
??A. 英雄出場后,會有3秒鐘的無敵時間,也就是在這3秒鐘內,任何敵機都無法摧毀英雄飛機
??B. 使用鍵盤的方向鍵可以在游戲窗口內移動英雄飛機(但不允許將英雄飛機移動到游戲窗口之外)
??C. 英雄出場后,每隔0.2秒,會自動連續發射3顆子彈,其中
???a. 子彈從英雄飛機頭部的正上方發射,沿垂直方向向游戲窗口的上方飛行
???b. 如果子彈飛行途中,擊中了敵機,會對敵機造成傷害
???c. 如果子彈飛出了游戲窗口,中途沒有擊中任何一架敵機,子彈會被銷毀
??D. 英雄出場后,會默認攜帶3顆炸彈
???a. 玩家按下鍵盤上的字母B會引爆炸彈,一旦引爆,游戲窗口中所有敵機都會被炸毀
???b. 引爆炸彈后,游戲窗口左下角的炸彈數量會相應減少,如果炸彈數量不足,則顯示數字0,同時不再允許玩家繼續引爆炸彈

(2)英雄飛機包含的屬性如表1所示:

表1 英雄飛機屬性表

名稱 速度 飛行動畫 被擊中圖片 被摧毀動畫 摧毀音效 升級音效
英雄 5 有 無 有 有 有

(3)子彈包含的屬性如表2所示:

表2 子彈屬性表

名稱 速度 傷害力 音效
子彈 1 2 1 有

2.3.2 敵機類型和關卡設定

(1)敵機類型
飛機大戰游戲中一共有三種類型的敵機,各自對應的屬性如表3所示:

表3 敵機屬性表


序號 名稱 生命值 速度 分值 飛行動畫 被擊中圖片 被摧毀動畫 摧毀音效
01 小敵機 1 1~7 1000 圖片 無 有 有
02 中敵機 6 1~3 6000 圖片 有 有 有
03 大敵機 15 1 15000 有 有 有 有

(2)關卡設定
飛機大戰游戲根據玩家的得分逐步提高難道,共設立3個關卡,具體的設定如表4所示:

表4 關卡級別表


序號 名稱 分值范圍 小敵機數量(速度) 中敵機數量(速度) 大敵機數量(速度)
01 關卡1 <10000 16(1~3) 0(1) 0(1)
02 關卡2 <50000 24(1~5) 2(1) 0(1)
03 關卡3 >=50000 32(1~7) 4(1~3) 2(1)

2.3.3 敵機登場

游戲開始后,根據不同的關卡,準備不同數量的敵機,敵機按照以下規則登場:
?(1)敵機的初始位置在游戲窗口上方的隨機位置
?(2)敵機按照各自不同的速度,沿垂直方向向游戲窗口的下方飛行
?(3)如果飛行途中,與英雄飛機相撞,那么:
? ?A. 英雄飛機被摧毀,同時播放被摧毀動畫及被摧毀音效
? ??a. 動畫播放過程中,不允許操作英雄飛機
?? ?b. 動畫播放完成后,游戲窗口右下角英雄的命數會相應減少。如果還有剩余 命數,那么在英雄犧牲的位置出現新的英雄繼續戰斗(新出場的英雄有3秒的無敵時間,在無敵時間內,英雄飛機不會被摧毀,同時也不會摧毀其他敵機),反之沒有剩余命數,則游戲結束
??B. 摧毀英雄飛機的敵機,同時要播放被摧毀動畫及被摧毀音效
??? a. 動畫播放過程中,敵機在屏幕上位置不會移動
??? b. 動畫播放完成后,敵機會被設置回初始狀態,跳轉到第(1)步繼續執行
?(4)如果飛行途中,被子彈擊中,則敵機的生命值減去子彈的傷害力:
??A. 如果敵機的生命值>0,顯示被擊中圖片(如果有),敵機繼續向屏幕下方飛行
??B. 如果敵機的生命值<=0,播放被撞毀動畫和音效
???a. 動畫播放過程中,敵機在屏幕上位置不會移動
???b. 動畫播放完成后,敵機會被設置回初始轉態,跳轉到(1)步繼續執行
?(5)如果敵機飛出了游戲窗口,中途沒有被摧毀,會被設置回初始狀態,跳轉到第(1)步繼續執行

2.3.4 游戲道具和獎勵

英雄飛機每得到100000分會被獎勵1次生命。此外,游戲開始每隔30秒會從游戲窗口上方隨機位置向下飛出游戲道具,具體道具作用如表5所示:

表5 道具作用表


序號 名稱 功能描述 速度 播放音效
01 炸彈補給 英雄飛機拾取后,炸彈數量加1 5 有
02 子彈增強 英雄飛機拾取后,發射的子彈改為雙排,持續時長20秒 5 有

2.3.5 游戲結束

當英雄飛機沒有剩余命數,無法再繼續進行戰斗則游戲結束。在英雄飛機被撞毀的動畫播放完成之后,會完成以下動作:
(1)整個游戲畫面靜止
(2)游戲窗口中央靠上位置顯示“Game Over!”文字
(3)”Game Over!”下方顯示最好得分,玩家可以對比左上角顯示的當前得分更新最好得分,并在下次游戲時顯示最新的最好得分
(4)在最好得分下面會有“Press spaceBar to continue”的提示語

2.4 游戲模塊

飛機大戰游戲項目中設計4個模塊,分別是:
(1)game.py主游戲模塊.封裝Game類并負責啟動游戲
(2)game_items.py游戲元素模塊,封裝英雄飛機、子彈、敵機、道具等游戲元素類,并定義全局變量
(3)game_hud.py游戲面板模塊,封裝指示器面板類,統一管理游戲狀態、游戲分數、炸彈提示、生命次數以及文本提示等與游戲數據或狀態相關的內容
(4)game_music.py游戲音樂模塊,封裝音樂播放器類,專門負責音樂和音效的播放

3.實現過程及代碼

3.1 游戲框架搭建

3.1.1 游戲類的設計

1.根據功能和整體框架,游戲Game類的設計如圖2所示:


?

圖2 Game類圖

2. 游戲的屬性
根據屬性的作用,又可以將游戲的屬性分為游戲屬性和精靈組屬性
(1)游戲屬性 在游戲對象中,除了常規的主窗口屬性和游戲狀態屬性之外,還定義了2個精靈相關的屬性以及1個音樂播放器屬性。在游戲開發中,通知把顯示圖像的對象叫做精靈Sprite。在pygame中精靈有兩個重要的屬性。
?A. image要顯示的圖像(surface對象)
?B. rect圖像要顯示在游戲窗口的矩形區域 在游戲窗口中看到的每一個獨立的圖像或者一行文本,都可以看做一個精靈Sprite對象,例如:英雄飛機、一顆子彈、一架敵機等

游戲對象中定義的游戲屬性列表如表6所示:

表6 游戲屬性表


序號 名稱 說明
01 main_window 游戲主窗口,初始大小為(480,700)
02 is_game_over 游戲結束標記,初始為False
03 is_pause 游戲暫停標記,初始為False
04 hero 英雄精靈,初始顯示在游戲窗口中間靠下位置
05 hud_panel 指示器面板,負責顯示和游戲狀態以及數據相關的內容,包括:狀態圖像、游戲得分、炸彈數量、英雄命數,以及在游戲暫?;蚪Y束時,顯示在游戲窗口中央位置的提示信息等
06 player 音樂播放器,負責播放背景音樂和游戲音效


(2)精靈組屬性
精靈組是保存了多個精靈對象的組。精靈組有兩個重要的應用場景:
?A. 一次性繪制或更新多個精靈
?B. 碰撞檢測。碰撞檢測就是檢測多個精靈之間是否發生碰撞,例如:子彈是否集中敵機、敵機是否撞到英雄飛機

游戲對象中定義的精靈組屬性列表如表7所示:

表7 精靈組屬性表


序號 名稱 說明
01 all_group 所有精靈組,存放所有要顯示的精靈,用于屏幕繪制和更新位置
02 enemies_group 敵機精靈組,存放所有敵機精靈對象,用于檢測子彈擊中敵機以及敵機撞擊英雄飛機
03 supplies_group 道具精靈組,存放所有道具機靈對象,用于檢測英雄飛機拾取道具


3. 游戲對象的方法
游戲類中為游戲對象封裝了如表8如示:

表8 游戲對象的方法表


序號 名稱 說明
01 reset_game 重置游戲,開啟新一輪游戲之前,將游戲屬性恢復到初始值
02 create_enemies 創建敵機,在新游戲開始或者關卡升級后,根據當前游戲級別創建敵機精靈
03 create_supplies 創建道具,游戲開始后每隔30秒隨機投放炸彈補給或子彈增強道具
04 start 開始游戲,創建時鐘對象并且開啟游戲循環,在游戲循環中監聽事件、更新精靈位置、繪制精靈、更新顯示、設置刷新幀率
05 event_handler 事件監聽,監聽并處理每一次游戲循環執行時發生的事件,避免游戲循環中的代碼過長
06 check_collide 碰撞檢測,監聽并處理每一個游戲循環執行時是否發生精靈與精靈之間的碰撞,其中包括:子彈擊中敵機、英雄拾取道具、敵機撞擊英雄等

3.1.2 搭建游戲框架

  • 在game_items.py模塊定義游戲窗口矩形區域的全局變量,代碼如下:
  • import pygame定義全局變量 SCREEN_RECT = pygame.Rect(0, 0, 480, 700) # 游戲窗口矩形
    • 1
    • 2
    • 3
  • 在game.py定義Game類,并實現初始化方法和重設游戲方法,
    (1). 在初始化方法中:
    ??A. 創建游戲窗口
    ??B. 設置窗口標題
    ??C. 設置游戲狀態屬性
    (2)定義reset_game 方法,恢復游戲狀態的初始值
    (3)定義event_handler方法監聽退出事件和監聽空格鍵按鍵并切換狀態
    (4)在start方法中:
    ??A. 定義時鐘對象
    ??B. 完成游戲循環的基礎代碼
    代碼如下:
  • ```python import pygame from game_hud import * from game_items import * from game_music import *class Game(object):"""游戲核心類"""def __init__(self):# 游戲窗口self.main_window = pygame.display.set_mode(SCREEN_RECT.size)pygame.display.set_caption('飛機大戰')#游戲狀態self.is_game_over = False # 結束標記self.is_game_pause = False # 暫停標記def reset_game(self):"""重置游戲數據"""self.is_game_over = Falseself.is_game_pause = Falsedef start(self):"""開啟游戲主邏輯"""# 創建時鐘clock = pygame.time.Clock()while True:# 處理事件監聽if self.event_handler():# event.handler 返回True則說明發生了退出事件return# 根據游戲轉態切換界面顯示的內容if self.is_game_over:print('游戲已經結束,按空格鍵重新開始游戲...')elif self.is_game_pause:print('游戲已經暫停,按空格鍵繼續...')else:print('游戲進行中...') #刷新頁面pygame.display.update()# 設置刷新率 clock.tick(60)def event_handler(self):"""獲取并處理事件"""for event in pygame.event.get():if event.type == pygame.QUIT:# 退出按鈕被點擊return Trueelif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:# 用戶按下了鍵盤上的 ESC 鍵return Trueelif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:# 用戶按下了鍵盤上的 空格 鍵if self.is_game_over:# 游戲已經結束,重置游戲self.reset_game()else:# 游戲還沒結束,切換暫停狀態self.is_game_pause = not self.is_game_pausereturn False
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
  • 在主程序中創建游戲對象并且啟動游戲
  • if __name__ == '__main__':#初始化數據pygame.init()#開始游戲Game().start()#釋放游戲資源pygame.quit()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.2 精靈與精靈組

    3.2.1 圖像的加載和繪制

  • 在game_items中創建派生游戲精靈子類
  • #定義精靈類 class GameSprite(pygame.sprite.Sprite):res_path = './res/images/'def __init__(self, image_name, speed, *group):"""初始化精靈對象"""# 調用父類方法,把當前精靈對象放到精靈組里super(GameSprite, self).__init__(*group)# 創建圖片self.image = pygame.image.load(self.res_path + image_name)# 獲取矩形self.rect = self.image.get_rect()# 設置移動速度self.speed = speeddef update(self, *args):"""更新元素數據"""self.rect.y += self.speed
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 在game.py中使用精靈組繪制精靈內容
  • class Game(object):"""游戲核心類"""def __init__(self):···# 游戲精靈組self.all_group = pygame.sprite.Group() # 存放界面上的所有精靈self.enemies_group = pygame.sprite.Group() # 敵機精靈組self.supplies_group = pygame.sprite.Group() # 道具精靈組# 游戲精靈GameSprite('background.png', 1, self.all_group) # 創建背景精靈hero_sprite = GameSprite('me1.png', 0, self)hero_sprite.rect.center = SCREEN_RECT.centerdef start(self):….# 繪制內容self.all_group.draw(self.main_window)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在start方法內更新精靈組

    def start(self):…while True:…else:print('游戲進行中...')self.all_group.update()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2.2 游戲背景連續滾動

    (1)基本構思
    游戲在運行的時候,整個游戲窗口顯示一張以星空為背景的圖像,同時背景圖像會緩緩地向下方移動,給玩家產生一種英雄飛機向上方飛行的錯覺
    背景圖像連續、不間斷地向下運動的實現,如示意圖3:

    圖3 背景示意圖



    具體實現:設置2張與屏幕大小相同的圖片,背景圖像1置于屏幕正中間,背景圖像2置于背景圖像1正上方,與背景圖像1相貼合,2張背景圖像沿屏幕向下移動,當背景圖像1移動出屏幕之外時,判斷圖像的y值是否大于或等于屏幕的高度h,如果是則背景圖像1移動到背景圖像2,此時背景圖像2置于正中間,如此循環。
    (2)代碼實現
    ?A. 初始化方法的is_alt參數:
    ??a. False表示第1個精靈,初始矩形區域應該和游戲窗口重疊
    ??b. True表示另一個精靈,初始矩形區域應該和游戲窗口的正上方
    ?B. 重寫update()
    ??a. 先調用父類方法,完成向下移動矩形區域的動作
    ??b. 然后判斷是否移出游戲窗口,如果是,將精靈設置到游戲窗口的正上方
    ?在game_items.py模塊中定義Background類繼承自GameSprite,代碼如下:

    class Background(GameSprite):def __init__(self, is_alt, *group):"""如果 is_alt 為True則在初始化時這個精靈顯示在窗口上方,False則顯示在窗口內部"""super(Background, self).__init__('background.png', 1, *group)if is_alt:self.rect.y = -self.rect.hdef update(self, *args):super(Background, self).update(*args)# 如果圖片已經滾動到底部,則立即回到窗口的最上面,供重新顯示if self.rect.y > self.rect.h:self.rect.y = -self.rect.y
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在game.py中的Game類中調用,代碼如下

    class Game(object):"""游戲核心類"""def __init__(self):…# 創建背景精靈self.all_group.add(Background(False), Background(True))
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.3 指示器面板

    在飛機大戰游戲中設計一個指示器面板,負責顯示和游戲狀態以及數據相關的內容,包括:狀態圖像、游戲得分、炸彈圖像、炸彈數量、英雄命數指示,以及在游戲暫停或結束時,顯示在游戲窗口中央位置的提示信息等。

    3.3.1 指示器面板類的設計

    根據游戲功能的需要,得指示器面板的類圖如圖4所示:

    圖4 指示器面板類圖


    指示器的示意圖如圖5所示:

    圖5 指示器示意圖

    3.3.2 指示器面板類的準備

  • 指示器面板類的創建
    在game_hud.py模塊中定義 HUDPanel類,并實現初始化方法,代碼如下:
  • """游戲控制面板/提示信息模塊""" import pygame class HUDPanel(object):"""所有面板精靈的控制類"""def __init__(self, display_group):# 基本數據self.score = 0 # 游戲得分self.lives_count = 3 # 生命計數self.level = 1 # 游戲級別self.best_score = 0 # 最好成績
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 設計狀態按鈕類
    按照游戲規則描述,玩家按下空格鍵暫?;蚶^續游戲,左上角狀態精靈的顯示圖像會發生相應的變化。在game_items.py中,從GameSprite中派生一個StatusButton類,專門處理狀態圖像的切換,代碼如下:
  • class StatusButton(GameSprite):"""狀態按鈕精靈類"""def __init__(self, image_names, *groups):"""image_names 接受一個元組,元組的0下標必須是暫停的圖片,1下標必須是運行的圖片"""super(StatusButton, self).__init__(image_names[0], 0, *groups)# 準備用于切換顯示的兩張圖片self.images = [pygame.image.load(self.res_path + name) for name in image_names]def switch_status(self, is_pause):"""根據是否暫停,切換要使用的圖片對象"""self.image = self.images[1 if is_pause else 0]
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 創建圖像精靈
    (1)在game_hud.py中的初始化方法上方,定義幾個屬性,用于設置精靈的矩形區域和標簽精靈的字體顏色,代碼如下:
  • class HUDPanel(object):"""所有面板精靈的控制類"""margin = 10 # 精靈之間的間距white = (255, 255, 255) # 白色gray = (64, 64, 64) # 灰色
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)在初始化方法的末尾增加以下代碼,創建精靈并設置顯示位置:

    def __init__(self, display_group):# 圖像精靈# 狀態精靈self.status_sprite = StatusButton(('pause_nor.png', 'resume_nor.png'), display_group)self.status_sprite.rect.top = self.marginself.status_sprite.rect.left = self.margin# 炸彈精靈self.bomb_sprite = GameSprite('bomb.png', 0, display_group)self.bomb_sprite.rect.x = self.marginself.bomb_sprite.rect.bottom = SCREEN_RECT.bottom - self.margin# 生命精靈self.lives_sprite = GameSprite('life.png', 0, display_group)self.lives_sprite.rect.right = self.lives_label.rect.left - self.marginself.lives_sprite.rect.bottom = SCREEN_RECT.bottom - self.margin
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (3)在game.py的初始化方法中添加創建游戲控制面板的代碼:

    class Game(object):"""游戲核心類"""def __init__(self):···# 創建游戲控制面板self.hud_panel = HUDPanel(self.all_group)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.3.3 使用精靈實現文本標簽

    ?文本標簽的顯示內容并不需要在每一次游戲循環執行時都發生變化,因此不需要重寫update()方法,同時,在游戲執行過程中,如果希望更改文本標簽的顯示內容,可以通過set_text方法重寫渲染文本圖像image并且更新矩形區域rect
    (1)使用自定義字體定義文本標簽精靈
    ?在game_items.py模塊中定義Label類繼承自pygame.sprite.Sprite,代碼如下:

    class Label(pygame.sprite.Sprite):"""標簽精靈類"""font_path = './res/font/MarkerFelt.ttc'def __init__(self, text, size, color, *groups):"""初始化標簽精靈的數據"""super(Label, self).__init__(*groups)# 創建字體對象self.font = pygame.font.Font(self.font_path, size)# 字體顏色self.color = color# 精靈屬性self.image = self.font.render(text, True, self.color)self.rect = self.image.get_rect()def set_text(self, text):"""更新顯示的文本內容"""self.image = self.font.render(text, True, self.color)self.rect = self.image.get_rect()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (2)創建指示器面板中的文本標簽精靈
    在game_hud.py中的HUDPanel類初始化方法的末尾增加代碼,創建指示器面板中的文本標簽精靈,代碼如下:

    #得分標簽self.score_label = Label('%d' % self.score, 32, self.gray, display_group)self.score_label.rect.midleft = (self.status_sprite.rect.right + self.margin,self.status_sprite.rect.centery)# 炸彈計數標簽self.bomb_label = Label('X 3', 32, self.gray, display_group)self.bomb_label.rect.midleft = (self.bomb_sprite.rect.right + self.margin,self.bomb_sprite.rect.centery)# 生命計數標簽self.lives_label = Label('X %d' % self.lives_count, 32, self.gray, display_group)self.lives_label.rect.midright = (SCREEN_RECT.right - self.margin,self.bomb_sprite.rect.centery)# 最好成績標簽self.best_label = Label('Best:%d' % self.best_score, 36, self.white)self.best_label.rect.center = SCREEN_RECT.center# 狀態標簽self.status_label = Label('Game Paused!', 48, self.white)self.status_label.rect.midbottom = (self.best_label.rect.centerx,self.best_label.rect.y - 2 * self.margin)# 提示標簽self.tip_label = Label('Press spaceBar to continue', 22, self.white)self.tip_label.rect.midtop = (self.best_label.rect.centerx,self.best_label.rect.bottom + 8 * self.margin)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.3.4 游戲數據修改

    1.顯示炸彈數量
    (1)英雄出場后,會默認攜帶3顆炸彈
    (2)玩家按下按鍵字母B會引爆炸彈,引爆炸彈后,游戲窗口左下角的炸彈數量應該相應減少
    在game_hud.py中的HUDPanel類中定義show_bomb方法,使用傳入的炸彈數量參數更新炸彈計數標簽的顯示,代碼如下:

    def show_bomb(self, count):"""修改炸彈數量 X count """self.bomb_label.set_text('X %d' % count)self.bomb_label.rect.midleft = (self.bomb_sprite.rect.right + self.margin,self.bomb_sprite.rect.centery)
    • 1
    • 2
    • 3
    • 4
    • 5

    在game.py中event_handler方法中添加使用炸彈事件

    def event_handler(self):"""獲取并處理事件"""for event in pygame.event.get():···# 必須在游戲結束也沒暫停才能執行的操作if not self.is_game_over and not self.is_game_pause:if event.type == pygame.KEYDOWN and event.key == pygame.K_b: self.hud_panel.show_bomb(self.hero_sprite.bomb_count)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 顯示生命計數
    (1)英雄飛機被敵機撞毀后,游戲窗口右下角的命數計數會相應減少
    (2)英雄飛機每得到100000分會被獎勵1條命,游戲窗口右下角的命數計數也會相應增加
    在game_hud.py中的HUDPanel類中定義show_lives方法,使用生命計數屬性值更新生命計數標簽的顯示,代碼如下:
  • def show_lives(self):"""顯示最新的生命計數為 X lives_count"""self.lives_label.set_text('X %d' % self.lives_count)# 修改生命計數精靈的位置self.lives_label.rect.midright = (SCREEN_RECT.right - self.margin,self.bomb_sprite.rect.centery)# 修改生命精靈的位置self.lives_sprite.rect.right = self.lives_label.rect.x - self.margin
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 增加游戲得分
    每當英雄飛機摧毀一架敵機之后,游戲得分就應該相應增加,并且摧毀不同類型的敵機增加的分值不同
    (1)生命計數 – 英雄飛機每得到100000分會被獎勵1條命
    (2)最好成績 – 如果當前游戲得分超過了歷史最好成績,應該以當前的游戲得分作為最好成績
    (3)游戲級別 – 不同的游戲級別,出場敵機的類型、數量和速度都不盡相同,而游戲得分和游戲級別的關系如下:
    ??A. 得分 < 10000(級別1)
    ??B. 得分 < 50000(級別2)
    ??C. 得分 >= 50000(級別3)
    在HUDPanel類的初始化方法上方,定義獎勵生命和游戲級別的類屬性
  • reward_score = 100000 # 獎勵分值level2_score = 10000 # 級別2分值level3_score = 50000 # 級別3分值
    • 1
    • 2
    • 3

    在HUDPanel類中定義并實現increase_score方法,代碼如下:

    def increase_score(self, enemy_score):"""增加得分,注意同時要處理增加生命、關卡升級、更新最好成績"""# 計算最新得分score = self.score + enemy_score# 判斷是否增加生命if score // self.reward_score != self.score // self.reward_score:self.lives_count += 1self.show_lives()self.score = score# 更新最好成績self.best_score = score if score > self.best_score else self.best_score# 計算最新關卡等級if score < self.level2_score:level = 1elif score < self.level3_score:level = 2else:level = 3is_upgrade = level != self.levelself.level = level# 更新得分的精靈顯示內容self.score_label.set_text('%d' % score)self.score_label.rect.midleft = (self.status_sprite.rect.right + self.margin,self.status_sprite.rect.centery)# 返回是否升級給游戲主邏輯return is_upgrade
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3.3.5 最好成績的文件讀寫

    將歷史最好歷史成績保存到record.txt文件中
    (1)在游戲退出前,調用指示器面板的save_best_score方法,將最好成績寫入到record.txt文件中
    (2)在指示器面板的初始化方法中調用load_best_score方法,從record.txt文件中讀取最好成績

  • 保存最好成績
    (1)在HUDPanel類的初始化方法上方,定義類屬性指定保存最好成績的文件名,代碼如下:
  • record_filename = "record.txt" # 最好成績文件名
    • 1

    (2)在HUDPanel類中定義save_best_score方法:代碼如下:

    def save_best_score(self):"""保存當前最好得分到文件里"""file = open(self.record_filename, 'w')file.write('%d' % self.best_score)file.close()
    • 1
    • 2
    • 3
    • 4
    • 5

    在game.py中的start方法中添加退出游戲之前保存最好成績事件

    while True:# 判斷英雄是否已經死亡self.is_game_over = self.hud_panel.lives_count == 0# 處理事件監聽if self.event_handler():# event.handler 返回True則說明發生了退出事件# 退出游戲之前要保存最好的成績self.hud_panel.save_best_score()return
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    (3)在HUDPanel類中定義load_best_score方法:代碼如下

    def load_best_score(self):"""從文件里重新加載最好得分"""try:# 讀取文件內容file = open(self.record_filename, 'r')content = file.read()file.close()# 轉換內容為數字self.best_score = int(content)except (FileNotFoundError, ValueError):print('讀取最高得分文件,發生異常')
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在HUDPanel類中的初始化方法末尾添加加載最好成績:代碼如下

    # 從文件里加載最好成績self.load_best_score()print('初始化控制面板,當前最好得分是:', self.best_score)
    • 1
    • 2
    • 3

    3.3.6 游戲狀態變化

    (1)面板暫停:在游戲暫?;蚪Y束時,在游戲窗口中央位置顯示提示信息,而面板中的其他數據不會再發生變化
    (2)面板恢復:在游戲執行期間,不顯示中央位置的提示信息,面板中其他提示標簽的數據,會隨者游戲的推進而變化

  • 精靈組的繪制順序,先創建指示器面板,再創建英雄精靈,代碼如下:
  • # 游戲精靈# 創建背景精靈self.all_group.add(Background(False), Background(True))# 創建游戲控制面板self.hud_panel = HUDPanel(self.all_group)# 創建英雄飛機精靈hero = GameSprite('me1.png', 0, self.all_group)hero.rect.center = SCREEN_RECT.center # 顯示在屏幕中央
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 暫停和恢復面板
    (1)在game_hud.py中的HUDPanel類中定義panel_pause方法,代碼如下:
  • def panel_paused(self, is_game_over, display_group):"""游戲停止,顯示提示信息。is_game_over為 True 則說明游戲結束,為 False 則說明游戲暫停"""# 判斷是否已經顯示了提示信息if display_group.has(self.best_label, self.status_label, self.tip_label):return# 根據游戲狀態生成提示文本status = 'Game Over!' if is_game_over else 'Game Paused!'tip = 'Press spaceBar to 'tip += 'play again.' if is_game_over else 'continue.'# 修改標簽精靈的文本內容self.best_label.set_text('Best:%d' % self.best_score)self.status_label.set_text(status)self.tip_label.set_text(tip)# 修正標簽精靈的位置self.best_label.rect.center = SCREEN_RECT.centerself.status_label.rect.midbottom = (self.best_label.rect.centerx,self.best_label.rect.y - 2 * self.margin)self.tip_label.rect.midtop = (self.best_label.rect.centerx,self.best_label.rect.bottom + 8 * self.margin)# 把標簽精靈添加到精靈組display_group.add(self.best_label, self.status_label, self.tip_label)# 修改狀態按鈕self.status_sprite.switch_status(True)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    (2)在game_hud.py中的HUDPanel類中定義panel_resume方法,代碼如下:

    def panel_resume(self, display_group):"""取消停止狀態,隱藏提示信息"""display_group.remove(self.best_label, self.status_label, self.tip_label)# 修改狀態按鈕self.status_sprite.switch_status(False)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (3)在game.py中的start方法中調用,代碼如下;

    def start(self):···while True:···# 根據游戲轉態切換界面顯示的內容if self.is_game_over:# print('游戲已經結束,按空格鍵重新開始游戲...')self.hud_panel.panel_paused(True, self.all_group)elif self.is_game_pause:# print('游戲已經暫停,按空格鍵繼續...')self.hud_panel.panel_paused(False, self.all_group)else:# print('游戲進行中...')# self.all_group.update()self.hud_panel.panel_resume(self.all_group)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.3.7 游戲結束后的重置面板

    (1)在game.py中game循環中添加判斷游戲是否結束的代碼,如下:

    while True:# 判斷英雄是否已經死亡self.is_game_over = self.hud_panel.lives_count == 0# 處理事件監聽if self.event_handler():# event.handler 返回True則說明發生了退出事件# 退出游戲之前要保存最好的成績self.hud_panel.save_best_score()return
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    (2)在game_hud.py中的HUDPanel類中定義reset_panel方法,代碼如下:

    def reset_panel(self):"""重置面板數據"""# 重置數據self.score = 0self.lives_count = 3# 重置精靈數據self.increase_score(0)self.show_bomb(3)self.show_lives()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (3)在game.py的reset_game重新游戲方法中調用,代碼如下:

    def reset_game(self):"""重置游戲數據"""self.is_game_over = Falseself.is_game_pause = False# 重置面板self.hud_panel.reset_panel()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.4 逐幀動畫的基本實現

    3.4.1 派生飛機類

    在game_items.py模塊中從GameSprite類中派生一個Plane類,代碼如下:

    class Plane(GameSprite):"""飛機精靈類"""def __init__(self, normal_names, speed, hp, value, wav_name, hurt_name, destroy_names, *group):"""飛機類的初始化"""super(Plane, self).__init__(normal_names[0], speed, *group)# 飛機基本屬性self.hp = hp # 當前生命值self.max_hp = hp # 初始生命值self.value = value # 分值self.wav_name = wav_name # 音效名# 正常狀態圖像列表self.normal_images = [pygame.image.load(self.res_path + name) for name in normal_names]self.normal_index = 0 # 正常狀態圖像索引self.hurt_image = pygame.image.load(self.res_path + hurt_name) # 受傷圖像# 摧毀狀態圖像列表self.destroy_images = [pygame.image.load(self.res_path + name) for name in destroy_names]self.destroy_index = 0 # 摧毀狀態索引def reset_plane(self):"""重置飛機"""self.hp = self.max_hpself.normal_index = 0self.destroy_index = 0self.image = self.normal_images[0]def update(self, *args):"""更新狀態,準備下一次要顯示的內容"""# 判斷是否要更新if not args[0]:returnif self.hp == self.max_hp:# 切換要顯示的圖片self.image = self.normal_images[self.normal_index]# 計算下一次顯示的索引count = len(self.normal_images)self.normal_index = (self.normal_index + 1) % countelif self.hp > 0:# 受傷self.image = self.hurt_imageelse:# 死亡if self.destroy_index < len(self.destroy_images):self.image = self.destroy_images[self.destroy_index]self.destroy_index += 1else:self.reset_plane()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    3.4.2 設置逐幀動畫頻率

    由于游戲循環的刷新幀率設置成了60幀/秒,導致玩家飛機噴射的動畫效果太快,設置計數器監聽,運行循環每執行10次才更換一次圖像,每秒更換6次圖像,從而減低逐幀動畫的頻率
    (1)在game_items.py模塊中的頂部定義一個全局變量,記錄逐幀動畫更新的間隔幀數,代碼如下:

    FRAME_INTERVAL = 10 # 逐幀動畫間隔幀數
    • 1

    (2)在game.py模塊的start方法中添加以下代碼:

    def start(self):"""開啟游戲主邏輯"""# 創建時鐘clock = pygame.time.Clock()# 動畫幀數計數器frame_count = 0while True:···else:# print('游戲進行中...')self.hud_panel.panel_resume(self.all_group) frame_count = (frame_count + 1) % FRAME_INTERVALself.all_group.update(frame_count == 0)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ##3.5 飛機類的設計與實現

    3.5.1 派生敵機類

  • 定義敵機類
    在game_items.py從Plane類中派生Enemy類,代碼如下:
  • class Enemy(Plane):"""敵人飛機"""def __init__(self, kind, max_speed, *groups):"""初始化敵人飛機"""self.kind = kindself.max_speed = max_speedif kind == 0:# 小敵機super(Enemy, self).__init__(['enemy1.png'], 1, 1, 1000,'enemy1_down.wav', 'enemy1.png',['enemy1_down%d.png' % i for i in range(1, 5)],*groups)elif kind == 1:# 中敵機super(Enemy, self).__init__(['enemy2.png'], 1, 6, 6000,'enemy2_down.wav', 'enemy2_hit.png',['enemy2_down%d.png' % i for i in range(1, 5)],*groups)elif kind == 2:# 大敵機super(Enemy, self).__init__(['enemy3_n1.png', 'enemy3_n2.png'], 1, 15, 15000,'enemy3_down.wav', 'enemy3_hit.png',['enemy3_down%d.png' % i for i in range(1, 7)],*groups)# 初始化飛機時,要讓飛機隨機的選擇一個位置顯示self.reset_plane()def reset_plane(self):"""重置敵人飛機"""super(Enemy, self).reset_plane()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 創建敵機精靈
    在game.py的Game類中定義創建敵機精靈的方法,代碼如下:
  • def create_enemies(self):"""創建敵機"""count = len(self.enemies_group.sprites())groups = (self.all_group, self.enemies_group)# 根據不同的關卡創建不同數量的敵機if self.hud_panel.level == 1 and count == 0:# 關卡1for i in range(16):Enemy(0, 3, *groups)elif self.hud_panel.level == 2 and count == 16:# 關卡2for enemy in self.enemies_group.sprites():enemy.max_speed = 5for i in range(8):Enemy(0, 5, *groups)for i in range(2):Enemy(1, 1, *groups)elif self.hud_panel.level == 3 and count == 26:# 關卡3for enemy in self.enemies_group.sprites():enemy.max_speed = 7 if enemy.kind == 0 else 3for i in range(8):Enemy(0, 7, *groups)for i in range(2):Enemy(1, 3, *groups)for i in range(2):Enemy(2, 1, *groups)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在game.py模塊的初始化方法初始化敵機,代碼如下:

    # 初始化敵機self.create_enemies()
    • 1
    • 2
  • 設置敵機精靈隨機位置和敵機飛行
    在屏幕上方再取一個屏幕的高度,敵機隨機位置的y值為 屏幕高度 – 敵機高度的范圍內,
    x值為 屏幕寬度 – 敵機寬度的范圍,這個范圍內隨機位置向屏幕下方移動
    即:
    水平方向取一個0~(SCREEN_RECT.w – self.rect.w)之間的隨機數,設置給敵機矩形區域的x
    垂直方向取一個0~(SCREEN_RECT.h – self.rect.h)之間的隨機數,再減去游戲窗口的高度,然后設置給敵機矩形區域的y
    (1)對Enemy類的reset_plane方法做調整,代碼如下:
  • def reset_plane(self):"""重置敵人飛機"""super(Enemy, self).reset_plane()# 敵人飛機的數據重置x = random.randint(0, SCREEN_RECT.w - self.rect.w)y = random.randint(0, SCREEN_RECT.h - self.rect.h) - SCREEN_RECT.hself.rect.topleft = (x, y)# 重置飛機的飛行速度self.speed = random.randint(1, self.max_speed)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    (2)修改Enemy類的update方法,代碼如下:

    def update(self, *args):"""更新飛機的位置信息"""super(Enemy, self).update(*args)# 根據血量判斷是否還要移動if self.hp > 0:self.rect.y += self.speed# 如果移動后已經到達屏幕外,則需要重置飛機if self.rect.y >= SCREEN_RECT.h:self.reset_plane()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.5.2 派生英雄飛機類

  • 創建英雄飛機類
    (1)在game_items.py模塊的頂部定義全局常量記錄英雄默認炸彈數量和英雄飛機默認位置
  • HERO_BOMB_COUNT = 3 # 英雄默認炸彈數量 HERO_DEFAULT_MID_BOTTOM = (SCREEN_RECT.centerx,SCREEN_RECT.bottom - 90) # 英雄默認初始位置
    • 1
    • 2
    • 3

    (2)在game_items.py模塊中從Plane類派生一個Hero類,代碼如下:

    class Hero(Plane):"""英雄飛機"""def __init__(self, *groups):"""初始化英雄飛機"""self.is_power = False # 是否無敵self.bomb_count = HERO_BOMB_COUNT # 炸彈數量self.bullets_kind = 0 # 子彈類型self.bullets_group = pygame.sprite.Group() # 子彈精靈組super(Hero, self).__init__(('me1.png', 'me2.png'),5, 1, 0, 'me_down.wav', 'me1.png',['me_destroy_%d.png' % x for x in range(1, 5)],*groups)self.rect.midbottom = HERO_DEFAULT_MID_BOTTOM # 創建好飛機之后要設置飛機位置為屏幕底部中間
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (3)在game.py模塊中的初始化方法修改創建英雄飛機精靈和修改顯示炸彈數量

    # 創建英雄飛機精靈self.hero_sprite = Hero(self.all_group)self.hud_panel.show_bomb(self.hero_sprite.bomb_count)
    • 1
    • 2
    • 3
  • 移動英雄飛機
    按照pygame坐標系的設定 ---- x軸沿水平方向向右,逐漸增加。右鍵 – 左鍵的值作為英雄水平移動的計數,再用基數乘以英雄飛機的速度,得到飛機在水平方向移動的距離,如表9所示:
  • 表9 水平移動基數表

    按下右鍵 按下左鍵 右鍵-左鍵 移動方向
    0 0 0 不移動
    1 0 1 向右
    0 -1 -1 向左
    1 1 0 不移動

    同理,得垂直移動基數表如表10所示

    表10 垂直移動基數表

    按下上鍵 按下下鍵 上鍵-下鍵 移動方向
    0 0 0 不移動
    1 0 1 向上
    0 -1 -1 向下
    1 1 0 不移動

    (1)在game.py模塊中的start方法中修改代碼:

    ···while True:···else:# print('游戲進行中...')self.hud_panel.panel_resume(self.all_group)# 處理長按事件keys = pygame.key.get_pressed() # get_pressed()得到一個元組,每一個按鍵在元組里都有一個下標,對應下標值為1則說明按鍵被按下move_hor = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT] # 水平的移動基數move_ver = keys[pygame.K_DOWN] - keys[pygame.K_UP] # 垂直的移動基數# 檢測碰撞self.check_collide()frame_count = (frame_count + 1) % FRAME_INTERVALself.all_group.update(frame_count == 0, move_hor, move_ver)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (2)在game_items.py模塊中英雄飛機類增加update方法,代碼如下:

    def update(self, *args):"""*args的0號下標說明是否要更新下一幀動畫,1號說明玩家飛機水平方向移動基數,2號說明玩家飛機垂直方向移動基數"""super(Hero, self).update(*args)if len(args) != 3 or self.hp <= 0:return# 屏幕邊緣的位置修正self.rect.x += args[1] * self.speedself.rect.x = 0 if self.rect.x < 0 else self.rect.xif self.rect.right > SCREEN_RECT.right:self.rect.right = SCREEN_RECT.rightself.rect.y += args[2] * self.speedself.rect.y = 0 if self.rect.y < 0 else self.rect.yif self.rect.bottom > SCREEN_RECT.bottom:self.rect.bottom = SCREEN_RECT.bottom
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 炸毀游戲窗口內部的敵機
    (1)在game_items.py模塊的Hero類中定義blowup方法,將出現在游戲窗口內的敵機全部引爆,同時計算并返回得分
  • def blowup(self, enemies_group):"""炸毀所有敵機,并返回得到的總分值"""# 判斷是否能夠發起引爆if self.bomb_count <= 0 or self.hp <= 0:return 0# 引爆所有敵機,累計得分self.bomb_count -= 1score = 0count = 0for enemy in enemies_group.sprites():if enemy.rect.bottom > 0:score += enemy.valueenemy.hp = 0count += 1print('炸毀了%d架敵機,獲取得分%d' % (count, score))return score
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (2)在event_handler方法中,監聽到玩家按下字母B時,讓英雄飛機調用blowup方法,并使用返回的分數
    ??A. 更新游戲得分
    ??B. 更新炸彈數量顯示
    ??C. 判斷是否升級,如果是,調用create_enemies方法創建更多敵機

    def event_handler(self):"""獲取并處理事件"""for event in pygame.event.get():···# 必須在游戲結束也沒暫停才能執行的操作if not self.is_game_over and not self.is_game_pause:if event.type == pygame.KEYDOWN and event.key == pygame.K_b:# 釋放一顆炸彈,并修改炸彈數量score = self.hero_sprite.blowup(self.enemies_group)self.hud_panel.show_bomb(self.hero_sprite.bomb_count)if self.hud_panel.increase_score(score):self.create_enemies()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.6 碰撞檢測

    3.6.1 創建碰撞檢測

    (1)在game.py中創建check_collide方法,代碼如下:

    def check_collide(self):"""檢查是否有碰撞"""if not self.hero_sprite.is_power:collide_enemies = pygame.sprite.spritecollide(self.hero_sprite, self.enemies_group,False, pygame.sprite.collide_mask)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)在game_items.py模塊中的GameSprite類的初始化方法中生成描邊,代碼如下:

    # 生成遮罩屬性,提高碰撞檢測的執行效率self.mask = pygame.mask.from_surface(self.image)
    • 1
    • 2

    (3)在game.py模塊中的循環調用碰撞方法,代碼如下:

    def start(self):···while True:···else:# print('游戲進行中...')self.hud_panel.panel_resume(self.all_group) # 檢測碰撞self.check_collide()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.6.2 敵機撞毀英雄飛機

    英雄飛機被撞毀,播放被撞毀動畫,動畫播放完成后,游戲窗口右下角的英雄命數相應減少
    (1)如果還有剩余命數,那么英雄飛機會在犧牲的位置出現新的英雄飛機進行戰斗,期間會有3秒的無敵時間
    (2)如果沒有剩余命數,則游戲結束
    撞毀英雄飛機的敵機,同樣要播放被撞毀動畫
    (1)動畫播放過程中,敵機在屏幕上位置不會移動
    (2)動畫播放完成后,敵機會被設置回初始狀態,再次從游戲窗口上方飛出加入戰斗

  • 英雄飛機被撞毀
    (1)修改check_collide方法中的代碼:
  • def check_collide(self):"""檢查是否有碰撞"""if not self.hero_sprite.is_power:collide_enemies = pygame.sprite.spritecollide(self.hero_sprite, self.enemies_group,False, pygame.sprite.collide_mask)# 過濾掉已經被摧毀的敵機collide_enemies = list(filter(lambda x: x.hp > 0, collide_enemies))# 撞毀玩家飛機if collide_enemies:self.hero_sprite.hp = 0# 撞毀敵人飛機for enemy in collide_enemies:enemy.hp = 0
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 發布自定義事件重置英雄飛機
    (1)在game_items.py模塊的頂部定義全局常量記錄英雄犧牲事件代號,代碼如下:
  • HERO_DEAD_EVENT = pygame.USEREVENT # 英雄犧牲事件 HERO_POWER_OFF_EVENT = pygame.USEREVENT + 1 # 取消英雄無敵事件
    • 1
    • 2

    (2)重新Hero類中的reset_plane方法,代碼如下:

    def reset_plane(self):"""重置玩家飛機數據"""super(Hero, self).reset_plane()self.is_power = Trueself.bomb_count = HERO_BOMB_COUNTself.bullets_kind = 0# 發布事件,讓游戲主邏輯更新界面pygame.event.post(pygame.event.Event(HERO_DEAD_EVENT))# 發布定時事件pygame.time.set_timer(HERO_POWER_OFF_EVENT, 3000)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (3)在game.py模塊的event_handler方法中,當游戲未結束也沒暫停時判斷事件,代碼如下:

    for event in pygame.event.get():···# 必須在游戲未結束也沒暫停才能執行的操作if not self.is_game_over and not self.is_game_pause: ··· elif event.type == HERO_DEAD_EVENT:# 玩家飛機死亡self.hud_panel.lives_count -= 1self.hud_panel.show_lives()self.hud_panel.show_bomb(self.hero_sprite.bomb_count)elif event.type == HERO_POWER_OFF_EVENT:self.hero_sprite.is_power = Falsepygame.time.set_timer(HERO_POWER_OFF_EVENT, 0) # 設置定時器延時為0,可以取消定時器
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    玩家飛機被摧毀,重置英雄飛機位置,在game.py模塊的reset_game方法中,代碼如下:

    def reset_game(self):# 重置英雄飛機位置self.hero_sprite.rect.midbottom = HERO_DEFAULT_MID_BOTTOM
    • 1
    • 2
    • 3

    3.6.3 英雄飛機發射子彈

    (1)英雄出場后,每隔0.2秒,會自動連續發射3顆子彈,其中:
    ??A. 子彈會從英雄飛機頭部的正上方發射,沿垂直方向向游戲窗口的上方飛行
    ??B. 如果子彈飛出了游戲窗口,中途沒有擊中任何一架敵機,子彈會被銷毀
    ??C. 如果子彈飛行途中,擊中了敵機,會對敵機造成傷害,敵機的生命值減去子彈的傷害度
    (2)如果英雄飛機拾取到子彈增強道具,發射的子彈會改為雙排,并且持續的時長為20秒

  • 設計子彈類
    在game_items.py模塊從GameSprite類派生一個Bulle類,代碼如下:
  • class Bullet(GameSprite):"""子彈類"""def __init__(self, kind, *group):"""初始化子彈數據"""image_name = 'bullet1.png' if kind == 0 else 'bullet2.png'super(Bullet, self).__init__(image_name, -12, *group)self.damage = 1 # 殺傷力def update(self, *args):"""更新子彈的數據"""super(Bullet, self).update(*args)# 飛出屏幕之外則需要銷毀子彈if self.rect.bottom < 0:self.kill()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 英雄飛機發射子彈
    在Hero類中定義fire方法,并利用定時器事件,實現英雄發射子彈的功能
    (1)在game_items.py模塊的頂部定義英雄飛機發射子彈事件的全局常量,代碼如下:
  • HERO_FIRE_EVENT = pygame.USEREVENT + 2 # 英雄發射子彈事件
    • 1

    ? ?(2)在Hero類的初始化方法中的末尾,設置英雄發射子彈的定時器事件,代碼如下:

    def __init__(self, *groups):pygame.time.set_timer(HERO_FIRE_EVENT, 200) # 創建玩家飛機之后每0.2秒激活一次發射子彈事件
    • 1
    • 2

    ? ?(3)在Hero類中定義fire方法并根據bullet_kind屬性發射子彈,子彈之間間距為15

    def fire(self, display_group):""""英雄飛機發射一輪新的子彈"""# 準備子彈要顯示到的組groups = (display_group, self.bullets_group)# 創建新子彈并定位for i in range(3):bullet1 = Bullet(self.bullets_kind, *groups)y = self.rect.y - i * 15# 判斷子彈類型if self.bullets_kind == 0:bullet1.rect.midbottom = (self.rect.centerx, y)else:bullet1.rect.midbottom = (self.rect.centerx - 20, y)# 再創建一顆子彈bullet2 = Bullet(self.bullets_kind, *groups)bullet2.rect.midbottom = (self.rect.centerx + 20, y)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (4)在game.py模塊的event_handler方法中監聽到英雄飛機發射子彈定時器事件后,讓英雄飛機調用發射子彈方法

    # 必須在游戲未結束也沒暫停才能執行的操作 if not self.is_game_over and not self.is_game_pause: ··· elif event.type == HERO_FIRE_EVENT:# 英雄飛機發射子彈定時事件self.hero_sprite.fire(self.all_group)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 子彈擊中敵機
    在Game類中check_collide方法中實現子彈擊中并摧毀敵機的功能
    子彈碰撞敵機的流程如圖6所示:
  • 圖6 子彈碰撞敵機流程圖
    (1)在Game類check_collide方法的末尾增加子彈擊中敵機的處理,代碼如下:

    # 子彈和敵機的碰撞分析hit_enemies=pygame.sprite.groupcollide(self.enemies_group, self.hero_sprite.bullets_group, False, False, pygame.sprite.collide_mask)for enemy in hit_enemies:# 已經被摧毀的飛機不需要再處理if enemy.hp <= 0:continuefor bullet in hit_enemies[enemy]:bullet.kill() # 銷毀子彈enemy.hp -= bullet.damage # 修改敵機生命值if enemy.hp > 0:continue # 如果敵機沒有被摧毀,則繼續遍歷下一個子彈# 當前這一顆子彈已經把敵機摧毀if self.hud_panel.increase_score(enemy.value):self.create_enemies()# 這個飛機已經被摧毀,不需要再遍歷下一顆子彈了break
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    (2)游戲的重置,銷毀敵機和子彈,在Game類的reset_game方法中末尾重置,代碼如下:

    # 銷毀所有的敵人飛機for enemy in self.enemies_group:enemy.kill()# 銷毀所有子彈for bullet in self.hero_sprite.bullets_group:bullet.kill()# 重新創建飛機self.create_enemies()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.6.4 英雄飛機拾取道具

    游戲過程中,每隔30秒會從游戲窗口上方隨機位置向下飛出游戲道具

  • 創建游戲道具
    在game_items.py中定義從GameSprite類繼承的Supply類,代碼如下:
  • class Supply(GameSprite):"""道具精靈"""def __init__(self, kind, *group):"""初始化道具屬性"""image_name = 'bomb_supply.png' if kind == 0 else 'bullet_supply.png'super(Supply, self).__init__(image_name, 5, *group)self.kind = kind # 道具類型self.wav_name = 'get_bomb.wav' if kind == 0 else 'get_bullet.wav' # 道具的音效self.rect.bottom = SCREEN_RECT.h # 道具的初始位置def update(self, *args):"""修改道具位置"""if self.rect.y > SCREEN_RECT.h: # 如果已經移動到屏幕之外則不需要繼續移動returnsuper(Supply, self).update(*args)def throw_supply(self):"""投放道具"""self.rect.bottom = 0 # 移動道具到窗口頂部self.rect.x = random.randint(0, SCREEN_RECT.w - self.rect.w)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 定時投放道具
    (1)在game_items.py模塊的頂部定義全局常量記錄投放道具的定時器事件,代碼如下:
  • THROW_SUPPLY_EVENT = pygame.USEREVENT + 3 # 投放道具事件
    • 1

    (2)在game.py的Game類中實現create_supplies方法

    def create_supply(self):"""初始化兩個道具,并開啟投放道具的定時器"""Supply(0, self.all_group, self.supplies_group)Supply(1, self.all_group, self.supplies_group)pygame.time.set_timer(THROW_SUPPLY_EVENT, 30000)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在初始化方法的末尾調用

    # 初始化道具 self.create_supply()
    • 1
    • 2

    (3)在事件監聽中監聽到投放道具定時器事件后,讓隨機道具調用投放道具方法

    elif event.type == THROW_SUPPLY_EVENT:# 隨機投放一個道具supply = random.choice(self.supplies_group.sprites())supply.throw_supply()
    • 1
    • 2
    • 3
    • 4
  • 英雄飛機拾取道具
    在Game類的check_collide方法擴展實現英雄飛機拾取道具的功能
    (1)在game_items.py模塊的頂部定義全局常量記錄關閉子彈增強的定時器事件,代碼如下:
  • BULLET_ENHANCED_OFF_EVENT = pygame.USEREVENT + 4 # 關閉子彈增強事件
    • 1

    (2)在Game類check_collide方法的末尾增加英雄飛機拾取道具的處理,代碼如下:

    # 檢查英雄飛機和道具的碰撞supplies = pygame.sprite.spritecollide(self.hero_sprite, self.supplies_group,False, pygame.sprite.collide_mask)if supplies:supply = supplies[0]self.player.play_sound(supply.wav_name) # 根據道具類型播放撿道具的音效# 根據道具類型產生不同的行為if supply.kind == 0:self.hero_sprite.bomb_count += 1self.hud_panel.show_bomb(self.hero_sprite.bomb_count)else:self.hero_sprite.bullets_kind = 1 # 修改英雄子彈為雙排子彈pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 20000) # 20秒之后重新變為單排子彈# 移動道具到屏幕之下supply.rect.y = SCREEN_RECT.h
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (3)在event_handler方法中監聽到關閉子彈增強的定時器事件

    elif event.type == BULLET_ENHANCED_OFF_EVENT:# 玩家使用雙排子彈的時間已經結束,需要恢復為單排子彈self.hero_sprite.bullets_kind = 0 # 恢復為單排子彈pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 0) # 取消定時
    • 1
    • 2
    • 3
    • 4

    3.7 音樂與音效

    3.7.1 加載和播放背景音樂

    (1)在game_music模塊中定義MusicPlayer類,代碼如下:

    """游戲音樂控制模塊""" import pygame import osclass MusicPlayer(object):"""音樂播放類"""res_path = './res/sound/'def __init__(self, music_file):"""初始化音樂播放器"""# 加載背景音樂pygame.mixer.music.load(self.res_path + music_file)pygame.mixer.music.set_volume(0.1)# 初始化音效的字典self.sound_dict = {}files = os.listdir(self.res_path)for file in files:if file == music_file: # 背景音樂不需要處理continuesound = pygame.mixer.Sound(self.res_path + file)self.sound_dict[file] = sound@staticmethoddef play_music():"""播放背景音樂"""pygame.mixer.music.play(-1)@staticmethoddef pause_music(is_pause):"""根據暫停狀態決定是否要播放背景音樂"""if is_pause:pygame.mixer.music.pause()else:pygame.mixer.music.unpause()def play_sound(self, wav_name):"""根據文件名,播放音效"""self.sound_dict[wav_name].play()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    (2)在Game類初始化方法中調用音樂類的背景音樂播放方法,代碼如下:

    # 音樂播放器self.player = MusicPlayer('game_music.ogg')self.player.play_music()
    • 1
    • 2
    • 3

    (3)在Game類的event_handler方法中切換背景音樂狀態

    else:# 游戲還沒結束,切換暫停狀態self.is_game_pause = not self.is_game_pauseself.player.pause_music(self.is_game_pause) # 切換背景音樂狀態
    • 1
    • 2
    • 3
    • 4

    3.7.1 加載和播放音效

    (1)發射子彈
    在event_handler方法中找到監聽發射子彈事件的分支,代碼如下:

    elif event.type == HERO_FIRE_EVENT:# 英雄飛機發射子彈定時事件self.player.play_sound('bullet.wav')self.hero_sprite.fire(self.all_group)
    • 1
    • 2
    • 3
    • 4

    (2)引爆炸彈
    在event_handler方法中找到監聽玩家按下字母B引爆炸彈的分支,代碼如下:

    # 必須在游戲未結束也沒暫停才能執行的操作if not self.is_game_over and not self.is_game_pause:if event.type == pygame.KEYDOWN and event.key == pygame.K_b:# 釋放一顆炸彈,并修改炸彈數量if self.hero_sprite.hp > 0 and self.hero_sprite.bomb_count > 0:self.player.play_sound('use_bomb.wav')
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (3)投放和拾取道具
    在event_handler方法中找到監聽投放道具事件的分支,代碼如下:

    elif event.type == THROW_SUPPLY_EVENT:# 隨機投放一個道具self.player.play_sound('supply.wav')supply = random.choice(self.supplies_group.sprites())supply.throw_supply()
    • 1
    • 2
    • 3
    • 4
    • 5

    (4)敵機爆炸
    在check_collide方法中,找到檢測敵機被子彈擊中部分的代碼,調整代碼如下:

    # 當前這一顆子彈已經把敵機摧毀if self.hud_panel.increase_score(enemy.value):self.player.play_sound('upgrade.wav')self.create_enemies()
    • 1
    • 2
    • 3
    • 4

    (5)英雄飛機爆炸
    在check_collide方法中,找到檢測英雄飛機和敵機碰撞部分的代碼,調整代碼如下:

    # 撞毀玩家飛機if collide_enemies:self.player.play_sound(self.hero_sprite.wav_name)self.hero_sprite.hp = 0
    • 1
    • 2
    • 3
    • 4

    4.實驗結果

    (1)游戲開始界面,如圖7所示

    圖7 游戲界面

    (2)游戲結束界面,如圖8所示:

    圖8 游戲結束

    (3)獲取道具,子彈增強,如圖9所示:

    圖9 子彈增強

    4)釋放炸彈,炸毀屏幕所有敵機,如圖10所示

    圖10 炸彈炸毀敵機

    (5)掉落道具和生命次數的增加,如圖11所示:

    圖11 掉落道具和生命次數增加

    5.總結和展望

    (1)飛機大戰游戲的完成參考了網上的視頻和資料,在學習過程中,明顯暴露了自己在學習上的不足,對python的學習不夠深入,對pygame的學習也僅僅是皮毛。
    (2)飛機大戰的功能簡單,希望在以后深入學習的過程中繼續完善游戲,給游戲增添一些更好玩的功能,例如敵機釋放子彈、獲取道具增加玩家飛機等功能。
    (3)在學習的過程中,不但認識到python語言的強大和簡潔,而且學習到pygame面向對象設計的思想,對以后的學習很有幫助。

    總結

    以上是生活随笔為你收集整理的基于pygame实现的飞机大战游戏的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。