1、pygame入門
一、準備工做python
安裝pygame:程序員
$ sudo pip3 install pygame
驗證是否安裝:框架
$ python3 -m pygame.examples.aliens
遊戲思路:dom
-
把一些 靜止的圖像 繪製到 遊戲窗口 中ide
-
根據 用戶的交互 或其餘狀況,移動 這些圖像,產生動畫效果函數
-
根據 圖像之間 是否發生重疊,判斷 敵機是否被摧毀 等其餘狀況工具
二、初始化和退出動畫
要使用 pygame
提供的全部功能以前,須要調用 init
方法ui
在遊戲結束前須要調用一下 quit
方法spa
方法 | 說明 |
---|---|
pygame.init() |
導入並初始化全部 pygame 模塊,使用其餘模塊以前,必須先調用 init 方法 |
pygame.quit() |
卸載全部 pygame 模塊,在遊戲結束以前調用! |
import pygame pygame.init() print("遊戲內容") pygame.quit()
三、遊戲中的座標系
原點 在 左上角 (0, 0)
x 軸 水平方向向 右,逐漸增長
y 軸 垂直方向向 下,逐漸增長
在遊戲中,全部可見的元素 都是以矩形區域來描述位置的
要描述一個矩形區域有四個要素:(x, y) (width, height)
pygame
專門提供了一個類 pygame.Rect
用於描述 矩形區域:
Rect(x, y, width, height) -> Rect
pygame.Rect
是一個比較特殊的類,內部只是封裝了一些數字計算,不執行 pygame.init()
方法一樣可以直接使用
描述英雄:
import pygame hero_rect = pygame.Rect(100, 500, 120, 125) print("英雄的原點:%d %d" % (hero_rect.x, hero_rect.y)) print("英雄的尺寸:%d %d" % (hero_rect.width, hero_rect.height)) # size屬性會返回矩形的(寬,高)元組 print("英雄大小:%d %d" % hero_rect.size)
四、建立遊戲主窗口
pygame提供了pygame.display模塊用於建立、管理遊戲窗口
方法 | 說明 |
---|---|
pygame.display.set_mode() |
初始化遊戲顯示窗口 |
pygame.display.update() |
刷新屏幕內容顯示 |
set_mode
方法:set_mode(size=(0,0), flags=0, depth=0) -> Surface
-
做用 —— 建立遊戲顯示窗口
-
參數
-
size
指定屏幕的寬
和高
,默認建立的窗口大小和屏幕大小一致 -
flags
參數指定屏幕的附加選項,例如是否全屏等等,默認不須要傳遞 -
depth
參數表示顏色的位數,默認自動匹配
-
-
返回值
-
surface,暫時 能夠理解爲 遊戲的屏幕,遊戲的元素 都須要被繪製到 遊戲的屏幕 上
-
-
注意:必須使用變量記錄
set_mode
方法的返回結果!由於:後續全部的圖像繪製都基於這個返回結果
import pygame pygame.init() # 建立遊戲窗口,並指定屏幕的寬高 screen = pygame.display.set_mode((480, 700)) # 遊戲循環,保證窗口不會由於代碼繼續向下執行而退出 while True: pass pygame.quit()
五、圖像繪製
保存在磁盤上的圖像文件首先應該被加載到內存:
要在屏幕上 看到某一個圖像的內容,須要按照三個步驟:
使用 pygame.image.load(file_path)
加載圖像的數據
使用 遊戲屏幕 對象,調用 blit(圖像,位置)
方法 將圖像繪製到指定位置
調用 pygame.display.update()
方法更新整個屏幕的顯示
(1)繪製背景圖像
加載background.png建立背景
將背景繪製在屏幕的(0,0)位置
更新屏幕顯示
import pygame pygame.init() screen = pygame.display.set_mode((480, 700)) # 加載圖像數據 bg = pygame.image.load("./images/background.png") # blit繪製圖像 screen.blit(bg, (0, 0)) # 更新屏幕顯示 pygame.display.update() while True: pass pygame.quit()
(2)繪製英雄圖像
加載me1.png建立英雄飛機
將飛機繪製在(200,500)位置
調用屏幕更新顯示飛機圖像
import pygame pygame.init() screen = pygame.display.set_mode((480, 700)) hero = pygame.image.load("./images/me1.png") screen.blit(hero, (200, 500)) pygame.display.update() while True: pass pygame.quit()
png
格式的圖像是支持透明的,在繪製圖像時,透明區域不會顯示任何內容。可是若是下方已經有內容,會透過透明區域顯示出來;
(3)案例調整
能夠在 screen
對象完成 全部 blit
方法以後,統一調用一次 display.update
方法,一樣能夠在屏幕上 看到最終的繪製結果
使用display.set_mode()建立的screen對象是一個內存中的屏幕數據對象,
screen.blit方法能夠在上面繪製不少圖像(這些圖像有可能會彼此重疊或覆蓋),
display.update()會將畫布的最終結果繪製在屏幕上,這樣能夠提升屏幕繪製效率,增長遊戲的流暢度;
import pygame pygame.init() screen = pygame.display.set_mode((480, 700)) bg = pygame.image.load("./images/background.png") hero = pygame.image.load("./images/me1.png") screen.blit(bg, (0, 0)) screen.blit(hero, (200, 500)) pygame.display.update() while True: pass pygame.quit()
六、遊戲循環和遊戲時鐘
跟電影的原理相似,遊戲中的動畫效果,本質上是快速的在屏幕上繪製圖像,每次繪製的結果被稱爲 幀Frame;
通常在電腦上每秒繪製60次,就能達到很是連續、高品質的動畫效果;
(1)遊戲循環
遊戲循環的開始就意味着遊戲的真正開始。
遊戲的兩個重要組成部分:
遊戲初始化(設置遊戲窗口、繪製圖像初始位置、設置遊戲時鐘)
遊戲循環(設置刷新幀率、檢測用戶交互、更新全部圖像位置、更新屏幕顯示)
遊戲循環的做用:
保證遊戲 不會直接退出
變化圖像位置 —— 動畫效果
每隔 1 / 60 秒
移動一下全部圖像的位置
調用 pygame.display.update()
更新屏幕顯示
檢測用戶交互 —— 按鍵、鼠標等...
(2)遊戲時鐘
pygame提供了pygame.time.Clock能夠很是方便地設置屏幕繪製速度——刷新幀率
要使用時鐘對象須要兩步:
在遊戲初始化建立一個時鐘對象
在遊戲循環中讓時鐘對象調用tick(幀率)方法
tick方法會根據上次被調用的時間,自動設置遊戲循環中的延時
clock = pygame.time.Clock() i = 0 while True: # 能夠指定循環體內部的代碼執行的頻率(每秒60次) clock.tick(60) print(i) i += 1
七、英雄的簡單動畫實現
需求:
在 遊戲初始化 定義一個 pygame.Rect
的變量記錄英雄的初始位置
在 遊戲循環 中每次讓 英雄 的 y - 1
—— 向上移動
y <= 0
將英雄移動到屏幕的底部
import pygame pygame.init() screen = pygame.display.set_mode((480, 700)) bg = pygame.image.load("./images/background.png") hero = pygame.image.load("./images/me1.png") screen.blit(bg, (0, 0)) screen.blit(hero, (150, 300)) pygame.display.update() clock = pygame.time.Clock() # 定義Rect記錄飛機的初始位置 hero_rect = pygame.Rect(150, 300, 102, 126) while True: clock.tick(60) # 修改飛機的位置 hero_rect.y -= 1 # 當飛機徹底飛出屏幕時,重置y值 if hero_rect.y + hero_rect.height <= 0: hero_rect.y = 700 # 從新繪製背景圖像,遮擋住上一次繪製的飛機圖案 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) pygame.display.update() pygame.quit()
每一次調用 update()
方法以前,須要把全部的遊戲圖像都從新繪製一遍,並且應該最早從新繪製背景圖像
八、在遊戲循環中監聽事件
事件Event:就是遊戲啓動後,用戶針對遊戲所作的操做,例如點擊關閉按鈕、點擊鼠標、按下鍵盤
監聽:在遊戲循環中,判斷用戶具體的操做,捕獲到用戶具體的操做,纔能有針對性的作出響應
在pygame中經過pygame.event.get()能夠得到用戶當前所作動做的事件列表(用戶能夠同一時間作不少事情)
這段代碼很是的固定,幾乎全部的pygame遊戲都大同小異
import pygame pygame.init() screen = pygame.display.set_mode((480, 700)) bg = pygame.image.load("./images/background.png") hero = pygame.image.load("./images/me1.png") screen.blit(bg, (0, 0)) screen.blit(hero, (150, 300)) pygame.display.update() clock = pygame.time.Clock() hero_rect = pygame.Rect(150, 300, 102, 126) while True: clock.tick(60) # 獲取動做事件列表 event_list = pygame.event.get() for event in event_list: # 判斷事件類型是不是用戶點擊了關閉按鈕 if event.type == pygame.QUIT: print("退出遊戲") pygame.quit() # 直接退出系統 exit() hero_rect.y -= 1 if hero_rect.y + hero_rect.height <= 0: hero_rect.y = 700 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) pygame.display.update() pygame.quit()
九、精靈和精靈組
在剛剛完成的案例中,圖像加載、位置變化、繪製圖像都須要程序員編寫代碼分別處理。
遊戲初始化(設置遊戲窗口、繪製圖像初始位置、設置遊戲時鐘)
遊戲循環(設置刷新幀率、檢測用戶交互、更新全部圖像位置、更新屏幕顯示)
爲了簡化開發步驟,pygame提供了兩個類:
pygame.sprite.Sprite 存儲圖像image和位置rect的對象
屬性:
image 要顯示的圖像
rect 圖像要顯示在屏幕的位置
方法:
update(*args) 在每次刷新屏幕時,更新精靈位置
kill() 從全部組中刪除
注意:pygame.sprite.Sprite
並無提供 image
和 rect
兩個屬性,須要程序員從 pygame.sprite.Sprite
派生子類,並在 子類 的 初始化方法 中,設置 image
和 rect
屬性
pygame.sprite.Group 精靈組,能夠包含多個精靈對象
方法:
__init__(self, *精靈)
add(*sprites) 向組中增長精靈
sprites() 返回全部精靈列表
update(*args) 讓組中全部精靈自動調用update方法
draw(Surface) 將組中全部精靈的image,繪製到Surface的rect位置
注意:仍然須要調用 pygame.display.update()
才能在屏幕看到最終結果
遊戲初始化:建立精靈、建立精靈組
遊戲循環:精靈組.update()、精靈組.draw()、pygame.display.update()
十、派生精靈子類
定義GameSprite繼承自pygame.sprite.Sprite
在重寫初始化方法時,必定要先 super()
一下父類的 __init__
方法,保證父類中實現的 __init__
代碼可以被正常執行
GameSprite
屬性:
image 精靈圖像,使用image_name加載
rect 精靈大小,默認使用圖像大小
speed 精靈移動速度,默認爲1
方法:
__init__(self, image_name, speed=1)
update(self) 每次更新屏幕時在遊戲內循環調用,讓精靈的self.rect.y += self.speed
注意:image
的 get_rect()
方法,能夠返回 pygame.Rect(0, 0, 圖像寬, 圖像高) 的對象
import pygame class GameSprite(pygame.sprite.Sprite): """飛機大戰遊戲精靈""" def __init__(self, image_name, speed=1): # 調用父類初始化方法 super().__init__(self) # 定義對象的屬性 self.image = pygame.image.load(image_name) self.rect = self.image.get_rect() self.speed = speed def update(self, *args): # 在屏幕的垂直方向上移動 self.rect.y += self.speed
十一、使用精靈和精靈組建立敵機
使用 from
導入 plane_sprites
模塊,from
導入的模塊能夠直接使用
在遊戲初始化建立精靈對象和精靈組對象
在遊戲循環中讓精靈組分別調用 update()
和 draw(screen)
方法
from plane_sprites import * import pygame pygame.init() screen = pygame.display.set_mode((480, 700)) bg = pygame.image.load("./images/background.png") hero = pygame.image.load("./images/me1.png") screen.blit(bg, (0, 0)) screen.blit(hero, (150, 300)) pygame.display.update() # 建立遊戲時鐘對象 clock = pygame.time.Clock() # 記錄英雄初始位置 hero_rect = pygame.Rect(150, 300, 102, 126) # 建立敵機的精靈 enemy1 = GameSprite("./images/enemy1.png") enemy2 = GameSprite("./images/enemy1.png", 2) # 建立敵機的精靈組 enemy_group = pygame.sprite.Group(enemy1, enemy2) while True: # 設置刷新頻率 clock.tick(60) # 檢測用戶交互 event_list = pygame.event.get() for event in event_list: if event.type == pygame.QUIT: print("退出遊戲...") pygame.quit() quit() # 修改飛機位置 hero_rect.y -= 1 # 判斷飛機位置 if hero_rect.y + hero_rect.height <= 0: hero_rect.y = 700 # 從新繪製圖像 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) # 讓精靈組調用兩個方法 # update - 讓組中的全部精靈更新位置 enemy_group.update() # draw - 在screen上繪製全部的精靈 enemy_group.draw(screen) pygame.display.update() pygame.quit()
2、飛機大戰
一、框架搭建
遊戲主程序能夠分爲:
遊戲初始化:
設置遊戲窗口
建立遊戲時鐘
建立精靈、精靈組
遊戲循環:
設置刷新頻率
事件監聽
碰撞檢測
更新/繪製精靈組
更新屏幕顯示
根據明確的職責,設計PlaneGame類:
屬性:
screen
clock
精靈組或精靈
方法:
__init__(self):
__create_sprites(self):
start_game(self):
__event_handler(self):
__check_collide(self):
__update_sprites(self):
__game_over():
-
遊戲初始化 ——
__init__()
會調用如下方法:
方法 | 職責 |
---|---|
__create_sprites(self) |
建立全部精靈和精靈組 |
-
遊戲循環 ——
start_game()
會調用如下方法:
方法 | 職責 |
---|---|
__event_handler(self) |
事件監聽 |
__check_collide(self) |
碰撞檢測 —— 子彈銷燬敵機、敵機撞毀英雄 |
__update_sprites(self) |
精靈組更新和繪製 |
__game_over() |
遊戲結束 |
plane_main文件:遊戲主程序(封裝主遊戲類、建立遊戲對象、啓動遊戲)
plane_sprites文件:封裝全部精靈子類、提供遊戲相關工具
plane_main.py
import pygame from plane_sprites import * class PlaneGame(object): """飛機大戰主遊戲""" def __init__(self): print("遊戲初始化...") # 建立遊戲的窗口 self.screen = pygame.display.set_mode(SCREEN_RECT.size) # 建立遊戲時鐘 self.clock = pygame.time.Clock() # 調用私有方法,建立精靈和精靈組 self.__create_sprites() def __create_sprites(self): pass 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() def __check_collide(self): pass def __update_sprites(self): pass @staticmethod def __game_over(): print("遊戲結束...") pygame.quit() exit() if __name__ == '__main__': # 建立遊戲對象 game = PlaneGame() # 啓動遊戲 game.start_game()
plane_sprites.py
import pygame # 屏幕大小的常量 SCREEN_RECT = pygame.Rect(0, 0, 480, 700) # 刷新的幀率 FRAME_PER_SEC = 60 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, *args): # 在屏幕的垂直方向上移動 self.rect.y += self.speed
二、背景圖像
交替滾動實現思路:
建立兩張背景圖像精靈,兩張圖像一塊兒向下方移動
self.rect.y += self.speed
當任意背景精靈的 rect.y>=屏幕的高度,說明已經移動到屏幕下方,將這張圖片設置到屏幕的上方
rect.y = -rect.height
GameSprite的update方法沒有針對移出屏幕進行判斷,咱們須要派生子類,重寫方法:
class Background(GameSprite): """遊戲背景精靈""" def update(self): # 1.調用父類的方法實現 super().update() # 2.判斷是否移出屏幕 if self.rect.y >= SCREEN_RECT.height: self.rect.y = -self.rect.height
def __create_sprites(self): # 建立背景精靈和精靈組 bg1 = Background("./images/background.png") bg2 = Background("./images/background.png") bg2.rect.y = -bg2.rect.height self.back_group = pygame.sprite.Group(bg1, bg2)
def __update_sprites(self): self.back_group.update() self.back_group.draw(self.screen)
上方代碼存在問題:
在主程序中,建立的兩個背景精靈,傳入了相同的圖像文件路徑
建立 第二個背景精靈 時,在主程序中設置背景精靈的圖像位置,而根據面向對象設計原則,應該由精靈本身負責
利用初始化方法,簡化背景精靈的建立:
-
直接指定 背景圖片
-
is_alt
判斷是不是另外一張圖像-
False
表示 第一張圖像,須要與屏幕重合 -
True
表示 另外一張圖像,在屏幕的正上方
-
def __create_sprites(self): # 建立背景精靈和精靈組 bg1 = Background() bg2 = Background(True) self.back_group = pygame.sprite.Group(bg1, bg2)
class Background(GameSprite): """遊戲背景精靈""" def __init__(self, is_alt=False): # 調用父類方法實現精靈的建立 super().__init__("./images/background.png") # 判斷是不是替換圖像,若是是,設置初始位置 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
三、定時器
定時器:
在 pygame
中可使用 pygame.time.set_timer()
來添加定時器
set_timer(eventid, milliseconds) -> None
所謂定時器,就是每隔一段時間,去執行一些動做
set_timer
能夠建立一個事件,能夠在遊戲循環的事件監聽方法中捕獲到該事件
第 1 個參數事件代號須要基於常量 pygame.USEREVENT
來指定,USEREVENT
是一個整數,再增長的事件可使用 USEREVENT + 1
指定,依次類推...
第 2 個參數是事件觸發間隔的毫秒值(1000毫秒=1秒)
定時器事件的監聽:
經過 pygame.event.get()
能夠獲取當前時刻全部的 事件列表
遍歷列表 而且判斷 event.type
是否等於 eventid
,若是相等,表示 定時器事件 發生
定義並監聽建立敵機的定時器事件
pygame
的 定時器 使用套路很是固定:
定義 定時器常量 —— eventid
# 建立敵機的定時器常量 CREATE_ENEMY_EVENT = pygame.USEREVENT
在 初始化方法 中,調用 set_timer
方法 設置定時器事件
# 設置定時器事件 pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000)
在 遊戲循環 中,監聽定時器事件
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("敵機出場...")
四、設計敵機類
敵機的特色:
每隔一秒出現一架
向屏幕下方飛行,速度各不相同
敵機出現的水平位置也不相同
敵機從屏幕下方飛出,不會再回到屏幕中
初始化方法:
指定敵機圖片
隨機敵機的初始位置和初始速度
update():
判斷是否飛出屏幕,若是是,從精靈組刪除
class Enemy(GameSprite): """敵機精靈""" def __init__(self): # 調用父類方法,建立敵機精靈,同時指定敵機圖片 super().__init__("./images/enemy1.png") # 指定敵機的初始隨機速度 # 指定敵機的初始隨機位置 def update(self): # 調用父類方法,保持垂直方向的飛行 super().update() # 判斷是否飛出屏幕,須要刪除飛出的精靈 if self.rect.y > SCREEN_RECT.height: print("飛出屏幕...")
在 __create_sprites
,添加 敵機精靈組。敵機是定時被建立的,所以在初始化方法中,不須要建立敵機
# 建立敵機精靈組 self.enemy_group = pygame.sprite.Group()
在 __event_handler
,建立敵機,而且 添加到精靈組。調用 精靈組 的 add
方法能夠 向精靈組添加精靈
elif event.type == CREATE_ENEMY_EVENT: # 建立敵機精靈 enemy = Enemy() # 將敵機精靈添加到敵機精靈組 self.enemy_group.add(enemy)
在 __update_sprites
,讓 敵機精靈組調用 update
和 draw
方法
self.enemy_group.update() self.enemy_group.draw(self.screen)
實現隨機敵機位置和速度:
def __init__(self): # 調用父類方法,建立敵機精靈,同時指定敵機圖片 super().__init__("./images/enemy1.png") # 指定敵機的初始隨機速度 self.speed = random.randint(1, 3) # 指定敵機的初始隨機位置 self.rect.x = random.randint(0, SCREEN_RECT.width-self.rect.width) # bottom屬性:底邊bottom=y+height # 能夠把bottom設爲0,實現敵機從-height位置飛入的效果 self.rect.bottom = 0
模塊導入順序
1) 官方標準模塊導入
2) 第三方模塊導入
3)應用程序模塊導入
銷燬飛出屏幕的敵機:
def update(self): # 調用父類方法,保持垂直方向的飛行 super().update() # 判斷是否飛出屏幕,須要刪除飛出的精靈 if self.rect.y > SCREEN_RECT.height: self.kill() # 從全部的精靈組中移除並自動銷燬 # 會在對象被銷燬時調用 def __del__(self): print("敵機飛出 %s" % self.rect)
五、設計英雄和子彈類
英雄需求:
遊戲啓動後,英雄出如今屏幕的水平中間位置,距離屏幕底部 120
像素
英雄每隔 0.5
秒發射一次子彈,每次連發三枚子彈
英雄默認不會移動,須要經過 左/右 方向鍵,控制英雄在水平方向移動
子彈需求:
子彈從英雄的正上方發射沿直線向上方飛行
飛出屏幕後,須要從精靈組中刪除
Hero
bullets屬性:
記錄全部子彈精靈
初始化方法:
指定英雄圖片
初始速度=0
定義bullets子彈精靈組保存子彈精靈
重寫update:
英雄須要水平移動
保證不能移出屏幕
fire方法:
用於發射子彈
Bullet
初始化方法:
指定子彈圖片
初始速度=-2(子彈向上方飛行)
重寫update:
判斷是否飛出屏幕,飛出就從精靈組刪除
繪製英雄:
class Hero(GameSprite): """英雄精靈""" def __init__(self): # 調用父類方法,設置英雄圖像和速度 super().__init__("./images/me1.png", 0) # 設置英雄的初始位置 self.rect.centerx = SCREEN_RECT.centerx self.rect.bottom = SCREEN_RECT.height - 120
# 建立英雄的精靈和精靈組 self.hero = Hero() # 要在其餘方法中使用,因此定義成屬性 self.hero_group = pygame.sprite.Group(self.hero)
self.hero_group.update() self.hero_group.draw(self.screen)
六、鍵盤按鍵捕獲
在Python中針對鍵盤按鍵的捕獲有兩種方式:
1)判斷event.type == pygame.KEYDOWN(KEYDOWN事件表示用戶按下了某個鍵)(K_RIGHT表示向右方向鍵)
elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: print("向右移動...")
2)首先使用pygame.key.get_pressed()返回全部按鍵元組,再經過鍵盤常量,判斷元組中某一個鍵是否被按下(若是被按下,對應數值爲1,沒有按下就是0)
# 返回全部按鍵的元組,若是某個鍵被按下,對應的值會是1 keys_pressed = pygame.key.get_pressed() # 判斷是否按下了方向鍵 if keys_pressed[pygame.K_RIGHT]: print("向右移動...")
第一種方式用戶必需要擡起按鍵纔算一次按鍵事件,操做靈活性大打折扣;
第二種方式用戶能夠按住方向鍵不放,實現朝某個方向持續移動;
七、英雄移動
在Hero類中重寫update方法:
讓英雄的rect.x與速度speed疊加
不須要調用父類方法,父類方法只實現了垂直運動
def update(self): # 英雄在水平方向移動 self.rect.x += self.speed
在__event_handler方法中根據左右方向鍵設置英雄的速度
向右 2
向左-2
沒有按鍵或其餘0
def __event_handler(self): for event in pygame.event.get(): # 判斷是否退出遊戲 if event.type == pygame.QUIT: PlaneGame.__game_over() # 監聽建立敵機的定時器事件 elif event.type == CREATE_ENEMY_EVENT: # 建立敵機精靈 enemy = Enemy() # 將敵機精靈添加到敵機精靈組 self.enemy_group.add(enemy) # 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 update(self): # 英雄在水平方向移動 self.rect.x += self.speed # 控制英雄不能離開屏幕 if self.rect.x < 0: self.rect.x = 0 elif self.rect.right > SCREEN_RECT.right: self.rect.right = SCREEN_RECT.right
八、發射子彈
英雄每隔0.5秒發射一次,每次連發三枚子彈
pygame的定時器使用套路:
定義定時器常量 eventid
在初始化方法中,set_timer設置定時器事件
在遊戲循環中,監聽定時器事件
def fire(self): print("發射子彈")
# 英雄發射子彈的定時器常量 HERO_FIRE_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(HERO_FIRE_EVENT, 500)
elif event.type == HERO_FIRE_EVENT: # 監聽發射子彈事件 self.hero.fire()
Bullet類:
初始化方法:指定圖片和初始速度-2
update:子彈飛出屏幕就刪除
class Bullet(GameSprite): """子彈精靈""" def __init__(self): # 調用父類方法設置子彈圖片,設置初始速度 super().__init__("./images/bullet1.png", -2) def update(self): # 調用父類方法,讓子彈沿垂直方向飛行 super().update() # 判斷子彈是否飛出 if self.rect.bottom < 0: self.kill() def __del__(self): print("子彈銷燬")
在 Hero
的 初始化方法 中建立 子彈精靈組 屬性
def __init__(self): # 調用父類方法,設置英雄圖像和速度 super().__init__("./images/me1.png", 0) # 設置英雄的初始位置 self.rect.centerx = SCREEN_RECT.centerx self.rect.bottom = SCREEN_RECT.height - 120 # 建立子彈的精靈組 self.bullets = pygame.sprite.Group()
修改 plane_main.py
的 __update_sprites
方法,讓 子彈精靈組 調用 update
和 draw
方法
self.hero.bullets.update() self.hero.bullets.draw(self.screen)
實現 fire()
方法:
建立子彈精靈
設置初始位置 —— 在 英雄的正上方
將 子彈 添加到精靈組
def fire(self): # 建立子彈精靈 bullet = Bullet() # 設置精靈位置 bullet.rect.bottom = self.rect.y - 20 bullet.rect.centerx = self.rect.centerx # 將精靈添加到精靈組 self.bullets.add(bullet)
一次發射三枚子彈:
def fire(self): for i in (0, 1, 2): # 建立子彈精靈 bullet = Bullet() # 設置精靈位置 bullet.rect.bottom = self.rect.y - i * 20 bullet.rect.centerx = self.rect.centerx # 將精靈添加到精靈組 self.bullets.add(bullet)
九、碰撞檢測
1)pygame.sprite.groupcollide():兩個精靈組 中 全部的精靈 的碰撞檢測
groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
-
若是將
dokill
設置爲True
,則 發生碰撞的精靈將被自動移除 -
collided
參數是用於 計算碰撞的回調函數-
若是沒有指定,則每一個精靈必須有一個
rect
屬性
-
# 子彈摧毀敵機 pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True)
2)pygame.sprite.spritecollide():判斷 某個精靈 和 指定精靈組 中的精靈的碰撞
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
-
若是將
dokill
設置爲True
,則 指定精靈組 中 發生碰撞的精靈將被自動移除 -
collided
參數是用於 計算碰撞的回調函數-
若是沒有指定,則每一個精靈必須有一個
rect
屬性
-
-
返回 精靈組 中跟 精靈 發生碰撞的 全部精靈列表
def __check_collide(self): # 子彈摧毀敵機 pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True) # 敵機撞毀英雄 enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True) # 判斷列表是否有內容 if len(enemies) > 0: # 讓英雄犧牲 print("英雄犧牲了...") self.hero.kill() # 結束遊戲 PlaneGame.__game_over()
3、飛機大戰代碼
plane_main.py
import pygame from plane_sprites import * class PlaneGame(object): """飛機大戰主遊戲""" def __init__(self): print("遊戲初始化...") # 建立遊戲的窗口 self.screen = pygame.display.set_mode(SCREEN_RECT.size) # 建立遊戲時鐘 self.clock = pygame.time.Clock() # 調用私有方法,建立精靈和精靈組 self.__create_sprites() # 設置定時器事件 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: # 建立敵機精靈 enemy = Enemy() # 將敵機精靈添加到敵機精靈組 self.enemy_group.add(enemy) # elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: # print("按下右方向鍵") elif event.type == HERO_FIRE_EVENT: # 監聽發射子彈事件 self.hero.fire() # 返回全部按鍵元組 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): # 子彈摧毀敵機 pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True) # 敵機撞毀英雄 enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True) # 判斷列表是否有內容 if len(enemies) > 0: # 讓英雄犧牲 print("英雄犧牲了...") 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()
plane_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): # 調用父類方法實現精靈的建立 super().__init__("./images/background.png") # 判斷是不是替換圖像,若是是,設置初始位置 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): # 調用父類方法,建立敵機精靈,同時指定敵機圖片 super().__init__("./images/enemy1.png") # 指定敵機的初始隨機速度 self.speed = random.randint(1, 3) # 指定敵機的初始隨機位置 self.rect.x = random.randint(0, SCREEN_RECT.width-self.rect.width) # bottom屬性:底邊bottom=y+height # 能夠把bottom設爲0,實現敵機從-height位置飛入的效果 self.rect.bottom = 0 def update(self): # 調用父類方法,保持垂直方向的飛行 super().update() # 判斷是否飛出屏幕,須要刪除飛出的精靈 if self.rect.y > SCREEN_RECT.height: self.kill() # 從全部的精靈組中移除並自動銷燬 # 會在對象被銷燬時調用 def __del__(self): # print("敵機飛出 %s" % self.rect) pass class Hero(GameSprite): """英雄精靈""" def __init__(self): # 調用父類方法,設置英雄圖像和速度 super().__init__("./images/me1.png", 0) # 設置英雄的初始位置 self.rect.centerx = SCREEN_RECT.centerx self.rect.bottom = SCREEN_RECT.height - 120 # 建立子彈的精靈組 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: self.rect.right = SCREEN_RECT.right def fire(self): for i in (0, 1, 2): # 建立子彈精靈 bullet = Bullet() # 設置精靈位置 bullet.rect.bottom = self.rect.y - i * 20 bullet.rect.centerx = self.rect.centerx # 將精靈添加到精靈組 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() # def __del__(self): # print("子彈銷燬")
。