1.4項目飛機大戰
1.4.1項目準備
1.4.1.1確定模塊pygame是否安裝成功
-
安裝pygame模塊時要注意python的版本是否有對應的pygame版本
- https://pypi.org/project/pygame/#files查看對應的python版本是否有對應的pygame版本(cp37就表示pygame支持的python版本爲python3.7),如果有就可以進行後續的安裝
- 如果當前python版本沒有對應的pygame版本(比如當前寫文檔的時間看python3.8版本的Mac版本就沒有對應的pygame版本,也就是說,Mac電腦如果安裝的python版本是3.8的,那麼就無法安裝pygame,此時就必須更改python的版本)
-
安裝pygame模塊
$ sudo pip3 install pygame
-
驗證是否安裝成功(安裝成功會有自帶的遊戲界面顯示)
$ python3 -m pygame.examples.aliens
1.4.1.2項目準備
- 新建飛機大戰項目
- 新建一個dt_01_pygame入門.py
- 導入遊戲素材圖片
1.4.2遊戲窗口和繪製圖像
1.4.2.1遊戲窗口
1.4.2.1.1遊戲的初始化和退出
-
在使用pygame提供的所有功能之前需要調用pygame的init方法,在遊戲結束前要調用一下quit方法
方法 說明 pygame.init() 導入並初始化所有的pygame模塊,使用其它模塊之前必須先調用init方法 pygame.quit() 卸載所有的pygame模塊,在遊戲結束之前調用(及時釋放內存中的空間)
1.4.2.1.2pygame.Rect描述矩形區域
-
在遊戲中,所有可見元素都是以矩形區域來描述位置的,要描述一個矩形區域有四個要素(x, y)(width, height),分別表示橫座標、縱座標、矩形的寬度、矩形的高度
-
pygame專門提供了一個類pygame.Rect用於描述矩形區域
Rect(x, y, width, height) # 創建對象
-
pygame.Rect是一個比較特殊的類,內部只是封裝了一些數字計算,不執行pygame.init()方法同樣可以直接使用
import pygame test = pygame.Rect(100, 200, 10, 20) print("對象的座標是:%d %d" % (test.x, test.y)) print("對象的大小是:%d %d" % (test.size)) # size屬性返回的是一個元祖,包含兩個內容:widht和height
1.4.2.1.3創建遊戲窗口和遊戲循環
-
創建遊戲主窗口和遊戲循環
-
pygame專門提供了一個模塊pygame.display用於創建、管理遊戲窗口
方法 說明 pygame.display.set_mode() 初始化遊戲顯示窗口 pygame.display.update() 刷新屏幕內容顯示 -
set_mode()方法
set_mode(resolution=(0,0), flags=0, depth=0) ->Surface # 三個參數是缺省參數,可以不傳值,set_mode()方法返回的是一個窗口 # resolution是一個元祖,兩個數字分別表示窗口是寬度和高度;flays參數用來指定窗口的附加選項,例如是否全屏;depth參數表示顏色的位數,默認自動匹配 # set_mode()方法的返回值必須要有變量進行記錄,因爲後期所有的操作都是基於這個返回結果的
-
遊戲循環防止窗口一閃後立刻退出
-
代碼實例:
import pygame pygame.init() main_windows = pygame.display.set_mode((100,100)) # 這裏(100,100)之前不能寫參數resolution,直接寫元祖表示窗口的寬度和高度就好 # 無限循環防止窗口一閃而過 while True: pass pygame.quit()
-
1.4.2.2繪製圖像
1.4.2.2.1繪製圖像的三個步驟
-
將圖像加載到內存
- 使用pygame.image.load()加載圖像的數據
-
使用屏幕對象,調用blit方法將加載的圖像繪製到指定的位置
-
調用pygame.display.update()方法更新屏幕的顯示
-
透明圖像:對於一些圖像周圍有虛線就表示這部分圖片是透明的,也就是說當將這類圖片插入到背景中時原背景會覆蓋虛線部分
-
可以在所有的圖片都繪製完畢後統一更新屏幕(即只需要調用一次update方法)
-
在遊戲中每一次調用update()方法產生的效果叫做幀,一般在電腦上每秒繪製60次(也就是每秒調用60次update方法)就能夠達到連續、高品質的動畫效果
-
代碼演練
import pygame pygame.init() screen = pygame.display.set_mode((480,700)) # 繪製背景圖像 bg = pygame.image.load("./images/background.png") screen.blit(bg, (0, 0)) # 繪製英雄飛機圖像 hero = pygame.image.load("./images/me1.png") screen.blit(hero, (200, 500)) pygame.display.update() # update方法只需調用一次 pygame.event.get() # 最新版的pygame需要添加此行代碼纔會更新圖片,否則圖片不會被插入顯示 # 上一行代碼不需要每次添加圖片都寫一次,只需要在最後一個圖片插入後寫一次即可 # 無限循環防止窗口一閃而過 while True: pass pygame.quit()
1.4.3遊戲循環和鍵盤事件
1.4.3.1遊戲循環
1.4.3.1.1相關概念介紹
- 遊戲循環意味着遊戲的正式開始
- 在遊戲開發中通常將遊戲分爲兩個部分:遊戲初始化(設置遊戲窗口、繪製圖像初始位置、設置遊戲時鐘)和遊戲循環(設置刷新頻率、檢測用戶交互、更新所有圖像的位置、更新屏幕顯示)
1.4.3.1.2利用時鐘設置遊戲循環的刷新幀率
-
創建時鐘對象:clock = pygame.time.Clock()
-
調用tick方法設置遊戲循環的幀率:clock.tick(60)
-
代碼實例
import pygame pygame.init() screen = pygame.display.set_mode((480,700)) # 繪製背景圖像 bg = pygame.image.load("./images/background.png") screen.blit(bg, (0, 0)) # 繪製英雄飛機圖像 hero = pygame.image.load("./images/me1.png") screen.blit(hero, (200, 500)) pygame.display.update() # update方法只需調用一次 pygame.event.get() # 最新版的pygame需要添加此行代碼纔會更新圖片,否則圖片不會被插入顯示 # 上一行代碼不需要每次添加圖片都寫一次,只需要在最後一個圖片插入後寫一次即可 # 無限循環防止窗口一閃而過 clock = pygame.time.Clock() # 創建時鐘對象 i = 0 while True: clock.tick(60) # 調用tick方法設置循環的執行頻率(括號內的數字意味着循環一秒執行多少次) print(i) i += 1 pygame.quit()
1.4.3.1.3英雄動畫效果實現
-
在遊戲循環中每一次調用update方法之前都需要重新繪製遊戲圖像,首先要重新繪製背景圖像
import pygame pygame.init() screen = pygame.display.set_mode((480,700)) # 繪製背景圖像 bg = pygame.image.load("./images/background.png") screen.blit(bg, (0, 0)) # 繪製英雄飛機圖像 hero = pygame.image.load("./images/me1.png") screen.blit(hero, (200, 500)) pygame.display.update() # update方法只需調用一次 pygame.event.get() # 最新版的pygame需要添加此行代碼纔會更新圖片,否則圖片不會被插入顯示 # 上一行代碼不需要每次添加圖片都寫一次,只需要在最後一個圖片插入後寫一次即可 # 無限循環防止窗口一閃而過 # 記錄英雄的初始位置 hero_rect = pygame.Rect(200, 500, 100, 126) clock = pygame.time.Clock() # 創建時鐘對象 while True: clock.tick(5) # 調用tick方法設置循環的執行頻率(括號內的數字意味着循環一秒執行多少次) if hero_rect.bottom > 0: # 判斷是否到達背景頂部,這裏bottom = y + height hero_rect.y -= 50 # 在遊戲循環中每一次調用update方法之前都需要重新繪製遊戲圖像,首先要重新繪製背景圖像 screen.blit(bg,(0, 0)) # 重新繪製背景 screen.blit(hero, hero_rect) # 這裏可以直接寫對象 pygame.display.update() pygame.event.get() else: # 實現英雄的循環出現 hero_rect.y = 700 # 讓英雄出現在背景底部 pygame.quit()
1.4.3.2事件監聽
1.4.3.2.1基本概念和相關方法
-
事件event:
- 就是遊戲啓動後,用戶針對有戲所做的操作
- 例如:點擊關閉按鈕,點擊鼠標,按下鍵盤
-
監聽:
- 在遊戲循環中判斷用戶的具體的操作
-
代碼實現
-
在python中通過pygame.event.get()可以獲取用戶當前所做的事件列表,一個用戶一個時間內可以做很多事情
-
代碼實現
import pygame pygame.init() screen = pygame.display.set_mode((480,700)) # 繪製背景圖像 bg = pygame.image.load("./images/background.png") screen.blit(bg, (0, 0)) # 繪製英雄飛機圖像 hero = pygame.image.load("./images/me1.png") screen.blit(hero, (200, 500)) pygame.display.update() # update方法只需調用一次 pygame.event.get() # 最新版的pygame需要添加此行代碼纔會更新圖片,否則圖片不會被插入顯示 # 上一行代碼不需要每次添加圖片都寫一次,只需要在最後一個圖片插入後寫一次即可 # 無限循環防止窗口一閃而過 # 記錄英雄的初始位置 hero_rect = pygame.Rect(200, 500, 100, 126) clock = pygame.time.Clock() # 創建時鐘對象 while True: clock.tick(5) # 調用tick方法設置循環的執行頻率(括號內的數字意味着循環一秒執行多少次) # 捕獲事件 event_list = pygame.event.get() # 返回的是一個列表 if len(event_list) > 0: # 判斷用戶是否作出操作 print(event_list) if hero_rect.bottom > 0: # 判斷是否到達背景頂部,這裏bottom = y + height hero_rect.y -= 50 # 在遊戲循環中每一次調用update方法之前都需要重新繪製遊戲圖像,首先要重新繪製背景圖像 screen.blit(bg,(0, 0)) # 重新繪製背景 screen.blit(hero, hero_rect) # 這裏可以直接寫對象 pygame.display.update() pygame.event.get() else: # 實現英雄的循環出現 hero_rect.y = 700 # 讓英雄出現在背景底部 pygame.quit()
-
1.4.3.2.2監聽退出事件並且退出遊戲
import pygame
pygame.init()
screen = pygame.display.set_mode((480,700))
# 繪製背景圖像
bg = pygame.image.load("./images/background.png")
screen.blit(bg, (0, 0))
# 繪製英雄飛機圖像
hero = pygame.image.load("./images/me1.png")
screen.blit(hero, (200, 500))
pygame.display.update() # update方法只需調用一次
pygame.event.get() # 最新版的pygame需要添加此行代碼纔會更新圖片,否則圖片不會被插入顯示
# 上一行代碼不需要每次添加圖片都寫一次,只需要在最後一個圖片插入後寫一次即可
# 無限循環防止窗口一閃而過
# 記錄英雄的初始位置
hero_rect = pygame.Rect(200, 500, 100, 126)
clock = pygame.time.Clock() # 創建時鐘對象
while True:
clock.tick(5) # 調用tick方法設置循環的執行頻率(括號內的數字意味着循環一秒執行多少次)
for event in pygame.event.get(): # 遍歷用戶的操作(每次調用該方法列表中存儲的是當前的操作,不會保存之前的操作)
if event.type == pygame.QUIT: # 判斷用戶的操作是否爲關閉操作
print("退出遊戲")
pygame.quit() # 卸載所有模塊
exit() # 終止當前正在運行的程序
if hero_rect.bottom > 0: # 判斷是否到達背景頂部,這裏bottom = y + height
hero_rect.y -= 50
# 在遊戲循環中每一次調用update方法之前都需要重新繪製遊戲圖像,首先要重新繪製背景圖像
screen.blit(bg,(0, 0)) # 重新繪製背景
screen.blit(hero, hero_rect) # 這裏可以直接寫對象
pygame.display.update()
pygame.event.get()
else: # 實現英雄的循環出現
hero_rect.y = 700 # 讓英雄出現在背景底部
1.4.4精靈和精靈組
1.4.4.1基本概念
-
爲了簡化開發步驟,pygame提供了兩個類:pygame.sprite.Sprite和pygame.sprite.Group,其中pygame.sprite.Sprite存儲了圖像數據image和位置rect的對象
-
現在,一個遊戲分爲兩個部分:遊戲初始化(創建精靈、創建精靈組)和遊戲循環(精靈組.update(),精靈組.draw(screen),pygame.display.update())
-
精靈和精靈組
精靈(需要派生子類) image:記錄圖像數據;rect:記錄在屏幕上的位置 update(*args):更新精靈位置;kill():從所有組中刪除 精靈組 _init_(self, *精靈):add(*sprites):向組中增加精靈;sprites():返回所有精靈列表;update(*args):讓組中所有精靈調用update方法;draw(Surface):將組中所有精靈的image繪製到Surface的rect位置
1.4.4.2派生精靈子類
-
代碼實現
import pygame # 創建精靈子類 class GameSprite(pygame.sprite.Sprite): """飛機大戰遊戲精靈""" def __init__(self, image_name, speed = 1): # 調用父類的初始化方法 super().__init__() # 定義對象的屬性 self.image = pygame.image.load(image_name) # 加載圖片到內存 # 下一行代碼的image表示屬性,get_rect()方法返回的圖像寬和圖像高就是image的寬和高 self.rect = self.image.get_rect() # get_rect()方法可以返回pygame.Rect(0,0,圖像寬,圖像高)的對象 self.speed = speed def update(self): # 在屏幕的垂直方向上移動 self.rect.y += self.speed print(GameSprite.__mro__)
-
說明:如果一個類的父類不是object(這裏的父類事名義上的父類),在重寫父類的__init__方法時,要使用super()主動調用父類的__init__方法,否則子類將無法使用父類__init__中的方法和屬性
1.4.4.3設置敵機精靈組
-
精靈類
import pygame # 創建精靈子類 class GameSprite(pygame.sprite.Sprite): """飛機大戰遊戲精靈""" def __init__(self, image_name, speed = 1): # 調用父類的初始化方法 super().__init__() # 定義對象的屬性 self.image = pygame.image.load(image_name) # 加載圖片到內存 # 下一行代碼的image表示屬性,get_rect()方法返回的圖像寬和圖像高就是image的寬和高 self.rect = self.image.get_rect() # get_rect()方法可以返回pygame.Rect(0,0,圖像寬,圖像高)的對象 self.speed = speed def update(self): # 在屏幕的垂直方向上移動 self.rect.y += self.speed
-
實現代碼
import pygame from test_01 import GameSprite pygame.init() screen = pygame.display.set_mode((480,700)) # 繪製背景圖像 bg = pygame.image.load("./images/background.png") screen.blit(bg, (0, 0)) # 繪製英雄飛機圖像 hero = pygame.image.load("./images/me1.png") screen.blit(hero, (200, 500)) pygame.display.update() # update方法只需調用一次 pygame.event.get() # 最新版的pygame需要添加此行代碼纔會更新圖片,否則圖片不會被插入顯示 # 上一行代碼不需要每次添加圖片都寫一次,只需要在最後一個圖片插入後寫一次即可 # 無限循環防止窗口一閃而過 # 記錄英雄的初始位置 hero_rect = pygame.Rect(200, 500, 100, 126) clock = pygame.time.Clock() # 創建時鐘對象 # 創建敵機的精靈 enemy_1 = GameSprite(image_name = "./images/enemy1.png", speed = 2) enemy_2 = GameSprite("./images/enemy1.png") # 創建敵機的精靈組 enemy_group = pygame.sprite.Group(enemy_1, enemy_2) # 初始化方法中的當前參數可以接受一個元祖 while True: for event in pygame.event.get(): # 遍歷用戶的操作(每次調用該方法列表中存儲的是當前的操作,不會保存之前的操作) if event.type == pygame.QUIT: # 判斷用戶的操作是否爲關閉操作 print("退出遊戲") pygame.quit() # 卸載所有模塊 exit() # 終止當前正在運行的程序 if hero_rect.bottom > 0: # 判斷是否到達背景頂部,這裏bottom = y + height hero_rect.y -= 3 # 在遊戲循環中每一次調用update方法之前都需要重新繪製遊戲圖像,首先要重新繪製背景圖像 screen.blit(bg,(0, 0)) # 重新繪製背景 screen.blit(hero, hero_rect) # 這裏可以直接寫對象 else: # 實現英雄的循環出現 hero_rect.y = 700 # 讓英雄出現在背景底部 clock.tick(60) # 調用tick方法設置循環的執行頻率(括號內的數字意味着循環一秒執行多少次) # 讓精靈組中的每個精靈更新位置(調用update方法) enemy_group.update() # 讓精靈組中的每個精靈都繪製到屏幕上 enemy_group.draw(screen) pygame.display.update()
1.4.5框架搭建
1.4.5.1明確類的相關設計
-
設置一個主程序plan_main用以實現遊戲;設置一個程序plan_sprites實現其它功能
-
plan_main
plan_main 封裝主遊戲類 創建遊戲對象 啓動遊戲 -
plan_sprites
plan_sprites 封裝遊戲中所有需要的精靈子類 提供遊戲的相關工具
-
-
設計一個主遊戲類PlanGame類,實現以下功能
PlanGame screen;clock;精靈組或精靈 _init_(self):遊戲初始化;__create_sptites(self):創建精靈;start_game(self):遊戲循環;__event_handler(self):事件監聽;__check_collide(self):碰撞檢測;__update_sprites(self):更新精靈組;__game_over():遊戲結束 -
遊戲初始化方法__init__()功能實現
遊戲初始化 設置遊戲窗口 創建遊戲時鐘 創建精靈、精靈組 -
遊戲循環方法start_game()功能實現
遊戲循環 設置刷新頻率 事件監聽 碰撞檢測 更新/繪製精靈組 更新屏幕顯示
1.4.5.2常量的定義
- 在python中沒有嚴格意義上的常量,只是通過命名的約束(通常所有字母都是大寫就看作爲常量),常量的單詞之間用下劃線連接;在實際開發中爲防止某一個值的需求一直變化導致需要修改程序中所有的原先初值,可以將這個值設置爲一個常量,當需求變化時只需要修改改常量的值即可而不需要修改原先所有的值
1.4.6背景圖像
1.4.6.1實現背景圖像的交替滾動
-
使用兩張圖像來實現背景圖像的交替滾動
-
設置背景類Background來繼承GameSprite類,同時拓展背景類來實現背景圖像的交替滾動實現的功能(重寫init方法時新增一個參數is_alt用來判斷圖像是否時交替圖像)
Background _init_(self, is_alt):update(self)
1.4.7敵人飛機
1.4.8英雄飛機
1.4.8.1針對鍵盤按鍵的捕獲
-
方式一:判斷event.type == pygame.KEYDOWN
實例: if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: print("向右移動") # event不是模塊而是獲取到的監聽事件,event.key == pygame.K_RIGHT是用來判斷用戶的按鍵是否是右鍵,這種方法如果一直按鍵不放是隻會識別爲只按下了一次鍵而不會識別爲連續按下多次鍵
-
方式二:首先使用pygame.key.get_pressed()返回所有按鍵元祖,然後通過鍵盤常量,判斷元祖中某一個鍵是否被按下(如果被按下,對應數值爲1)
實例: keys_pressed = pygame.key.get_pressed() if keys_pressed[pygame.K_RIGHT]: # 這裏的pygame.K_RIGHT是元祖的索引 print("向右移動") # 這種方法如果一直按鍵不放會識別爲連續按下多次鍵
1.4.9發射子彈以及碰撞檢測
1.4.9.1定時器使用套路
-
第一步:定義計時器常量eventid(一般用USEREVENT來表示第一個事件,後面如果要增加事件就+1即可)
-
第二步:在初始化方法中調用set_timer方法設置定時器事件
-
第三步:在遊戲循環中監聽定時器事件
-
代碼實例:
# 第一步:定義計時器常量 CREATE_ENEMY_EVENT = pygame.USEREVENT # 定時器常量是一個整數 # 第二步:設置定時器事件 pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000) # 1000表示毫秒,表示每個1秒觸發一次事件CREATE_ENEMY_EVENT # 第三步:循環監聽定時器事件 def __event__handleer(self): for event in pygame.event.get(): if event.type == CREATE_ENEMY_EVENT: self.hero.fire()
1.4.9.2碰撞檢測
-
pygame的兩種實現碰撞的方法
-
第一種方法:pygame.sprite.groupcollide()
兩個精靈組的所有精靈的碰撞檢測 groupcollide(group1, group2, dokill1, dokill2, collided = None) --> Sprite_dict # kokill1和group1有關,kokill2和group2有關;如果kokill1設置爲True則如果group1發生碰撞則會將group1銷燬 # collided參數用來計算碰撞的回調函數,如果沒有指定則每個精靈必須有一個rect屬性
-
第二種方法:pygame.sprite.spritecollide()
spritecollide(sprite, group, dokill, collided = None) --> Sprite_list # 當dokill設置爲True時,如果sprite發生碰撞則sprite會被銷燬,返回的是精靈組中和精靈發生碰撞的精靈列表
-
1.4.10完整代碼實現
-
plan_main.py
import pygame from plane_sprites import * class PlaneGame(object): """飛機大戰主遊戲""" def __init__(self): print("遊戲初始化") # 1. 創建遊戲的窗口 self.screen = pygame.display.set_mode(SCREEN_RECT.size) # 2. 創建遊戲的時鐘 self.clock = pygame.time.Clock() # 3. 調用私有方法,精靈和精靈組的創建 self.__create_sprites() # 4. 設置定時器事件 - 創建敵機 1s pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000) pygame.time.set_timer(HERO_FIRE_EVENT, 500) def __create_sprites(self): # 創建背景精靈和精靈組 bg1 = Background() bg2 = Background(True) self.back_group = pygame.sprite.Group(bg1, bg2) # 創建敵機的精靈組 self.enemy_group = pygame.sprite.Group() # 創建英雄的精靈和精靈組 self.hero = Hero() self.hero_group = pygame.sprite.Group(self.hero) def start_game(self): print("遊戲開始...") while True: # 1. 設置刷新幀率 self.clock.tick(FRAME_PER_SEC) # 2. 事件監聽 self.__event_handler() # 3. 碰撞檢測 self.__check_collide() # 4. 更新/繪製精靈組 self.__update_sprites() # 5. 更新顯示 pygame.display.update() def __event_handler(self): for event in pygame.event.get(): # 判斷是否退出遊戲 if event.type == pygame.QUIT: PlaneGame.__game_over() elif event.type == CREATE_ENEMY_EVENT: # print("敵機出場...") # 創建敵機精靈 enemy = Enemy() # 將敵機精靈添加到敵機精靈組 self.enemy_group.add(enemy) elif event.type == HERO_FIRE_EVENT: self.hero.fire() # elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: # print("向右移動...") # 使用鍵盤提供的方法獲取鍵盤按鍵 - 按鍵元組 keys_pressed = pygame.key.get_pressed() # 判斷元組中對應的按鍵索引值 1 if keys_pressed[pygame.K_RIGHT]: self.hero.speed = 2 elif keys_pressed[pygame.K_LEFT]: self.hero.speed = -2 else: self.hero.speed = 0 def __check_collide(self): # 1. 子彈摧毀敵機 pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True) # 2. 敵機撞毀英雄 enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True) # 判斷列表時候有內容 if len(enemies) > 0: # 讓英雄犧牲 self.hero.kill() # 結束遊戲 PlaneGame.__game_over() def __update_sprites(self): self.back_group.update() self.back_group.draw(self.screen) self.enemy_group.update() self.enemy_group.draw(self.screen) self.hero_group.update() self.hero_group.draw(self.screen) self.hero.bullets.update() self.hero.bullets.draw(self.screen) @staticmethod def __game_over(): print("遊戲結束") pygame.quit() exit() if __name__ == '__main__': # 創建遊戲對象 game = PlaneGame() # 啓動遊戲 game.start_game()
-
plan_sprites.py
import random import pygame # 屏幕大小的常量 SCREEN_RECT = pygame.Rect(0, 0, 480, 700) # 刷新的幀率 FRAME_PER_SEC = 60 # 創建敵機的定時器常量 CREATE_ENEMY_EVENT = pygame.USEREVENT # 定時器常量是一個整數 # 英雄發射子彈事件(定義發射子彈事件的常量) HERO_FIRE_EVENT = pygame.USEREVENT + 1 class GameSprite(pygame.sprite.Sprite): """飛機大戰遊戲精靈""" def __init__(self, image_name, speed=1): # 調用父類的初始化方法 super().__init__() # 定義對象的屬性 self.image = pygame.image.load(image_name) self.rect = self.image.get_rect() self.speed = speed def update(self): # 在屏幕的垂直方向上移動 self.rect.y += self.speed class Background(GameSprite): """遊戲背景精靈""" def __init__(self, is_alt=False): # 1. 調用父類方法實現精靈的創建(image/rect/speed) super().__init__("./images/background.png") # 2. 判斷是否是交替圖像,如果是,需要設置初始位置 if is_alt: self.rect.y = -self.rect.height def update(self): # 1. 調用父類的方法實現 super().update() # 2. 判斷是否移出屏幕,如果移出屏幕,將圖像設置到屏幕的上方 if self.rect.y >= SCREEN_RECT.height: self.rect.y = -self.rect.height class Enemy(GameSprite): """敵機精靈""" def __init__(self): # 1. 調用父類方法,創建敵機精靈,同時指定敵機圖片 super().__init__("./images/enemy1.png") # 2. 指定敵機的初始隨機速度 1 ~ 3 self.speed = random.randint(1, 3) # 3. 指定敵機的初始隨機位置 self.rect.bottom = 0 max_x = SCREEN_RECT.width - self.rect.width self.rect.x = random.randint(0, max_x) def update(self): # 1. 調用父類方法,保持垂直方向的飛行 super().update() # 2. 判斷是否飛出屏幕,如果是,需要從精靈組刪除敵機 if self.rect.y >= SCREEN_RECT.height: # print("飛出屏幕,需要從精靈組刪除...") # kill方法可以將精靈從所有精靈組中移出,精靈就會被自動銷燬 self.kill() def __del__(self): # print("敵機掛了 %s" % self.rect) pass class Hero(GameSprite): """英雄精靈""" def __init__(self): # 1. 調用父類方法,設置image&speed super().__init__("./images/me1.png", 0) # 2. 設置英雄的初始位置 # centerx表示居中屬性(就是表示x軸的中心位置),表示飛船x方向中心與屏幕x方向中心重合,也即x方向居中 self.rect.centerx = SCREEN_RECT.centerx self.rect.bottom = SCREEN_RECT.bottom - 120 # bottom = y + height # 3. 創建子彈的精靈組 self.bullets = pygame.sprite.Group() def update(self): # 英雄在水平方向移動 self.rect.x += self.speed # 控制英雄不能離開屏幕 # 保證英雄不能從左側移出 if self.rect.x < 0: self.rect.x = 0 # 保證英雄不能從右側移出 elif self.rect.right > SCREEN_RECT.right: # right = x + width self.rect.right = SCREEN_RECT.right def fire(self): print("發射子彈...") # 一次發射三顆子彈 for i in (0, 1, 2): # 1. 創建子彈精靈 bullet = Bullet() # 2. 設置精靈的位置 bullet.rect.bottom = self.rect.y - i * 20 bullet.rect.centerx = self.rect.centerx # 3. 將精靈添加到精靈組 self.bullets.add(bullet) class Bullet(GameSprite): """子彈精靈""" def __init__(self): # 調用父類方法,設置子彈圖片,設置初始速度 super().__init__("./images/bullet1.png", -2) def update(self): # 調用父類方法,讓子彈沿垂直方向飛行 super().update() # 判斷子彈是否飛出屏幕 if self.rect.bottom < 0: self.kill() # kill()方法可以將精靈從精靈組中刪除 def __del__(self): print("子彈被銷燬...")