Python筆記(三)pygame飛機大戰案例

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 並無提供 imagerect 兩個屬性,須要程序員從 pygame.sprite.Sprite 派生子類,並在 子類 的 初始化方法 中,設置 imagerect 屬性

 

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

注意:imageget_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,讓 敵機精靈組調用 updatedraw 方法

 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 方法,讓 子彈精靈組 調用 updatedraw 方法

 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("子彈銷燬")

相關文章
相關標籤/搜索