黑馬python入門(6):Python基礎(pygame介紹)


Pygame 官方文檔 - 中譯版(感謝分享)

https://blog.csdn.net/Enderman_xiaohei/article/details/87708373


pygame模塊


pygame的初始化和退出

import pygame  # 導入
pygame.init()  # 初始化
# 主代碼內容
this_screen = pygame.Rect(100, 120, 400, 600)
print("this_screen.x:[{}]".format(this_screen.x))

pygame.quit()  # 退出 執行到這里就會把所有pygame相關模塊都徹底清理


pygame的Rect的介紹

作用:是存儲和操作一個pygame的矩形對象
定義:Rect(left, top, width, height)   Rect((left, top), (width, height))   Rect(object)  定義需要獲取坐標和寬高四個數值
常見屬性: x,y(就是獲取了矩形坐標和寬高 自動計算出來下面的這些屬性 也可以直接修改這些屬性來調整矩形的位置
top, left, bottom, right
topleft(返回左上坐標元組), bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery
size, width, height
w,h
常見方法
pygame.Rect.copy - 復制矩形(rectangle)
pygame.Rect.move - 移動矩形
pygame.Rect.move_ip - 就在原地位置移動矩形
pygame.Rect.inflate - 增大或縮小矩形大小
pygame.Rect.inflate_ip - 就在原地位置增大或縮小矩形大小
pygame.Rect.clamp - 將矩形移動到另一個內部
pygame.Rect.clamp_ip - 將矩形就在原地位置移動到另一個內部
pygame.Rect.clip - 在另一個內部裁剪一個矩形
pygame.Rect.union - 將兩個矩形連接成一個
pygame.Rect.union_ip - 將兩個矩形就在原地位置連接成一個
pygame.Rect.unionall - 許多矩形的並集
pygame.Rect.unionall_ip - 許多矩形就在原地位置並集
pygame.Rect.fit - 使用縱橫比調整大小並移動的矩形
pygame.Rect.normalize - 更正負大小
pygame.Rect.contains - 測試一個矩形是否在另一個矩形內
pygame.Rect.collidepoint - 測試一個點是否在矩形內
pygame.Rect.colliderect - 測試兩個矩形是否重疊
pygame.Rect.collidelist - 測試列表中的一個矩形是否相交
pygame.Rect.collidelistall - 測試列表中的所有矩形是否相交
pygame.Rect.collidedict - 測試字典中的一個矩形是否相交
pygame.Rect.collidedictall - 測試字典中的所有矩形是否相交
相關操作
1.獲取這個圖片的Rect對象 pygame.image.load('images/ship2.bmp').get_rect()
2.通過圖片的Rect對象的x y 屬性的變化來移動圖片對象

 
 
 
         
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# author:albert time:2020/9/17
import pygame
# import os
pygame.init()  # 初始化

# 參數配置
win_x = 480  # 游戲窗口大小
win_y = 700
FPS = 50  # FPS常量

# 需要的圖片路徑
me1_path = "images/me1.png"  # 我方飛機圖的路徑
enemy1_path = "images/enemy1.png"  # 敵機圖的路徑
background_path = "images/background.png"  # 背景圖的路徑

# 把需要用到的圖片都載入進來作為Surface對象 備用
me1_surface = pygame.image.load(me1_path)
enemy1_surface = pygame.image.load(enemy1_path)
background_surface = pygame.image.load(background_path)

# 獲取每個圖像的Rect對象 以便后面控制圖像變動位置等等
me1_rect = me1_surface.get_rect()
enemy1_rect = enemy1_surface.get_rect()
background_rect = background_surface.get_rect()

# 設置各個對象的初始坐標 從Surface對象那邊獲取的寬高是對的 但是坐標是0,0 所以這里要設置我機的初始坐標
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))
me1_rect.x = int((win_x - me1_rect.w)/2)  # 我機起始在最下面那行的中間位置開始
me1_rect.y = win_y - me1_rect.h
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))

# 創建游戲窗口 本質還是創建圖像或者surface對象
windows1 = pygame.display.set_mode((win_x, win_y), 0, 32)

# 一開始的游戲界面繪圖
windows1.blit(background_surface, (0, 0))  # 描繪背景
windows1.blit(me1_surface, me1_rect)  # 描繪我機
pygame.display.update()  # 刷新窗口界面

# 游戲流程開始
clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0
while True:

    clock.tick(FPS)  # 每次執行到這里就會自動重置

    # 無限循環留下一個退出的通道
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # 描繪背景
    windows1.blit(background_surface, (0, 0))
    # 計算我機本次的移動位置 到了最上面就自動回到原點重新跑
    me1_rect.y -= 1
    if me1_rect.y + me1_rect.h <= 0:
        me1_rect.y = win_y - me1_rect.h
    # 移動本機
    windows1.blit(me1_surface, me1_rect)
    pygame.display.update()  # 刷新界面

    print("[{}]:耗費{}毫秒,飛機坐標為({}, {})".format(i, clock.get_time(), me1_rect.x, me1_rect.y))
    i += 1

pygame.quit()  # 退出

pygame.Surface簡單理解

是用來存儲和表示pygame的圖像對象。 游戲窗口從本質上說也是我們繪制出來的圖像 在游戲窗口上運行的游戲 其實不過是 在一個大圖像上的各種小圖像相互作用的效果

(1)pygame.Surface.blit() 把圖像繪制到另外一個圖像上

pygame.Surface.blit(source, dest, area=None, special_flags = 0) –> Rect

source 參數指定的 Surface 對象繪制到該對象上。dest 參數指定繪制的位置,dest 的值可以是 source 的左上角坐標。如果傳入一個 Rect 對象給 dest,那么 blit() 會使用它的左上角坐標,而不關 Rect 的大小什么事_可選參數 area 是一個 Rect 對象,表示限定 source 指定的 Surface 對象的范圍。

(2)pygame.Surface.get_rect() 獲取surface對象的Rect

get_rect(**kwargs) –> Rect

注意返回的Rect對象可以通過操縱該對象的參數來自由的移動對應的圖像對象


pygame.image簡單理解

包含用於加載和保存圖片的函數,以及將Surface對象轉換為其他包可用的格式
(1)pygame.image.load(filename) –> Surface  返回的也是surface對象  從文件中加載新圖像(最常用的函數)
pygame.image.load(fileobj, namehint="") –> Surface


Pygame.display簡單理解

Pygame中用於控制窗口和屏幕顯示的模塊

(1)pygame.display.set_mode 初始化一個窗口

pygame.display.set_mode(resolution=(0,0), flags=0, depth=0) –> Surface

第一個參數是元組 描繪圖像的寬高 其他兩個可以省略 不用管

(2)pygame.display.update 更新界面顯示

你在窗口上描繪了很多圖像 但是這還不夠 還需要調用update刷新界面  這些描繪才能顯示出來

pygame.display.update(rectangle=None) –> None  一般而言不用加參數
pygame.display.update(rectangle_list) -> None


pygame.time的簡單介紹

pygame中用於管理時間的模塊

(1)幀數概念

動畫的本質是什么  就是一組連續的畫順序播放才形成了視覺上的動畫  我們做的游戲 動畫 很多時候 原理就是這么簡單

幀數:我們一秒鍾播放這動畫100張圖 就是幀數為100 一秒鍾播放這動畫50張圖 就是幀數為50 每張圖消耗的時間就是200ms

(2)pygame.time.Clock() 創建一個時鍾對象幫助跟蹤時間 Clock對象還提供了幾個函數來幫助控制游戲的幀速率 注意Clock是大寫C

Clock 按照寫法規定 這是一個類 其下的類方法有

pygame.time.Clock.tick - 更新clock對象
pygame.time.Clock.tick_busy_loop - 更新clock對象
pygame.time.Clock.get_time - 上一個tick中使用的時間
pygame.time.Clock.get_rawtime - 上一個tick中使用的實際時間
pygame.time.Clock.get_fps - 計算clock對象的幀率

常用的形式:

clock = pygame.time.clock()  # 聲明一個clock類的實例

while true:  # 一個無限循環

     # 主代碼部分 負責在游戲窗口上繪制里面的圖像的變化

     clock.tick(50)   # 實例的tick方法 參數是表示當前最高的幀數 一秒鍾刷新多少多少次游戲窗口的各個圖像對象 比如這個設置為50就是1秒鍾最高刷新50次游戲窗口 也就是200毫秒刷新一次 或者200毫秒循環一次 如果本次循環主代碼消耗時間不夠200毫秒 執行到了clock.tick(50)這里 會根據情況自動補足200毫秒 當然了 如果消耗超過200毫秒 clock.tick(50)什么也不干 直接下個循環

clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0
while True:

    clock.tick(FPS)  # 每次執行到這里就會自動重置

    # 無限循環留下一個退出的通道 避免無限循環卡頓
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # # 描繪背景
    # windows1.blit(background_surface, (0, 0))
    # # 計算我機本次的移動位置 到了最上面就自動回到原點重新跑
    # me1_rect.y -= 1
    # if me1_rect.y + me1_rect.h <= 0:
    #     me1_rect.y = win_y - me1_rect.h
    # # 移動本機
    # windows1.blit(me1_surface, me1_rect)
    # pygame.display.update()  # 刷新界面
    # 
    # print("[{}]:耗費{}毫秒,飛機坐標為({}, {})".format(i, clock.get_time(), me1_rect.x, me1_rect.y))
    i += 1

(3)pygame的定時器  就是其他語言里面的時鍾觸發  設置一個定時器定時有規律的重復執行某個操作 比較常見的定時器使用是配合自定義事件

設置一個自定義事件 使用pygame.USEREVENT來創建 類似端口號之類的 我們自定義的事件都要在這個pygame.USEREVENT這個常量之后的數字才不會和固有事件產生沖突

在代碼初始化中設置一個定時器和相關操作(這里是觸發自定義事件)

在事件監聽代碼中監聽指定的自定義事件並對他做出反應

import pygame
pygame.init()
# 定義自己的事件 注意一般都是常量
MY_EVENT1 = pygame.USEREVENT + 1

# 初始化中設置定時器 參數1是要定時觸發的事件id 后面是間隔多久觸發一次 這里是2秒一次
pygame.time.set_timer(MY_EVENT1, 2000)

clock = pygame.time.Clock()  # 聲明一個時鍾對象
# 接收事件
while True:
    clock.tick(10)  # fps
    # 避免卡頓的固定用法
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
        # 監聽自定義事件
        if event.type == MY_EVENT1:
            print("自定義觸發了")
pygame.quit()


pygame.event的簡單介紹

事件隊列 pygame.event.get()  所有的事件都在這個函數里面以字典的形式被捕捉了 我們要找特定的事件只需要遍歷這個字典來查找即可

pygame.event.wait()這個用的不多 是讓整個代碼都停下來等待用戶觸發一個事件才會繼續后續

常用事件


QUIT 用戶按下關閉按鈕 none
ATIVEEVENT Pygame被激活或者隱藏 gain, state
KEYDOWN 鍵盤被按下 unicode, key, mod
KEYUP 鍵盤被放開 key, mod
MOUSEMOTION 鼠標移動 pos, rel, buttons
MOUSEBUTTONDOWN 鼠標按下 pos, button
MOUSEBUTTONUP 鼠標放開 pos, button
VIDEORESIZE Pygame窗口縮放 size, w, h
USEREVENT 觸發了一個用戶事件 code

[<Event(1-ActiveEvent {'gain': 1, 'state': 1})>

自定義事件

常常和定時器配合使用

import pygame
pygame.init()
# 定義自己的事件 注意一般都是常量
MY_EVENT1 = pygame.USEREVENT + 1

# 初始化中設置定時器 參數1是要定時觸發的事件id 后面是間隔多久觸發一次 這里是2秒一次
pygame.time.set_timer(MY_EVENT1, 2000)

clock = pygame.time.Clock()  # 聲明一個時鍾對象
# 接收事件
while True:
    clock.tick(10)  # fps
    # 避免卡頓的固定用法
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
        # 監聽自定義事件
        if event.type == MY_EVENT1:
            print("自定義觸發了")
pygame.quit()


常用鼠標事件

[<Event(4-MouseMotion {'pos': (197, 101), 'rel': (-1, 2), 'buttons': (0, 0, 0), 'window': None})>] 鼠標移動反饋

[<Event(4-MouseMotion {'pos': (226, 298), 'rel': (-1, 1), 'buttons': (1, 0, 0), 'window': None})>] 點擊鼠標左鍵反饋

[<Event(4-MouseMotion {'pos': (268, 312), 'rel': (0, 1), 'buttons': (0, 0, 1), 'window': None})>] 點擊鼠標右鍵反饋

[<Event(5-MouseButtonDown {'pos': (268, 312), 'button': 2, 'window': None})>] 鼠標按下反饋

[<Event(6-MouseButtonUp {'pos': (234, 319), 'button': 1, 'window': None})>] 鼠標松開

MOUSEMOTION事件會在鼠標動作的時候發生,它有三個參數:

  • buttons – 一個含有三個數字的元組,三個值分別代表左鍵、中鍵和右鍵,1就是按下了。
  • pos – 就是位置了……
  • rel – 代表了現在距離上次產生鼠標事件時的距離

和MOUSEMOTION類似的,我們還有MOUSEBUTTONDOWNMOUSEBUTTONUP兩個事件,看名字就明白是什么意思了。很多時候,你只需要知道鼠標點下就可以了,那就可以不用上面那個比較強大(也比較復雜)的事件了。它們的參數為:

  • button – 看清楚少了個s,這個值代表了哪個按鍵被操作
  • pos – 和上面一樣。


利用鼠標各個事件實現一個鼠標按住拖拽圖像移動 松開了放開圖像的操作

原理

鼠標按住事件中:檢測到鼠標按住的時候 通過鼠標按住事件獲取到按下鼠標的坐標 然后判斷鼠標位置是否在圖像的范圍內 不在圖像范圍內自然就不用實現拖拽功能,如果在圖像范圍內 則設置一個開關變量 在鼠標按住時間中設置為True 允許拖拽 

鼠標松開事件中:在鼠標松開事件中開關變量設置為False 不允許拖拽。

鼠標移動事件中:具體拖拽行為的實現是要在鼠標移動事件里面,先檢測從鼠標按住事件和鼠標松開事件檢測過的開關變量的值為真為假  是否允許拖拽圖像,得到允許后 使用鼠標移動事件獲取當前的鼠標坐標  修改圖像對象的Rect屬性來調整位置 實現拖拽移動圖像

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# author:albert time:2020/9/17
import pygame

# import os
pygame.init()  # 初始化

# 參數配置
win_x = 480  # 游戲窗口大小
win_y = 700
FPS = 60  # FPS常量

# 需要的圖片路徑 照理說這里應該檢測下文件是否存在 文件格式等等 不過為了演示不糾結這個
me1_path = "images/me1.png"  # 我方飛機圖的路徑
enemy1_path = "images/enemy1.png"  # 敵機圖的路徑
background_path = "images/background.png"  # 背景圖的路徑

# 把需要用到的圖片都載入進來作為Surface對象 備用
me1_surface = pygame.image.load(me1_path)
enemy1_surface = pygame.image.load(enemy1_path)
background_surface = pygame.image.load(background_path)

# 獲取每個圖像的Rect對象 以便后面控制圖像變動位置等等
me1_rect = me1_surface.get_rect()
enemy1_rect = enemy1_surface.get_rect()
background_rect = background_surface.get_rect()

# 設置各個對象的初始坐標 從Surface對象那邊獲取的寬高是對的 但是坐標是0,0 所以這里要設置我機的初始坐標
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))
me1_rect.x = int((win_x - me1_rect.w) / 2)  # 我機起始在最下面那行的中間位置開始
me1_rect.y = win_y - me1_rect.h
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))

# 創建游戲窗口 本質還是創建圖像或者surface對象
windows1 = pygame.display.set_mode((win_x, win_y), 0, 32)

# 一開始的游戲界面繪圖
windows1.blit(background_surface, (0, 0))  # 描繪背景
windows1.blit(me1_surface, me1_rect)  # 描繪我機
pygame.display.update()  # 刷新窗口界面

# 游戲流程開始
clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0  # 區別每個循環的計數君
is_move = False  # (不能聲明在循環內)這個是一個是否允許拖拽圖像的開關變量 按下鼠標 判斷當前鼠標位置在圖像內 則允許拖拽 松開鼠標則關閉拖拽 具體的拖拽行為由移動鼠標事件負責


while True:

    clock.tick(FPS)  # fps

    # 描繪背景
    windows1.blit(background_surface, (0, 0))

    # 臨時坐標 用來接收鼠標位置
    tmp_x = 0
    tmp_y = 0

    # 開始遍歷事件做針對處理 同時避免卡頓
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

        # 如果當前鼠標按下並且鼠標位置在圖像內 就表示可以拖拽了 開關變量為True
        if event.type == pygame.MOUSEBUTTONDOWN:  # 按下鼠標

            tmp_x, tmp_y = event.pos  # 記錄鼠標當前位置並判斷是否在圖像范圍內 是則可以拖拽 不在則不可以
            if ((tmp_x >= me1_rect.x)
                    and (tmp_x <= me1_rect.x + me1_rect.w)
                    and (tmp_y >= me1_rect.y)
                    and (tmp_y <= me1_rect.y + me1_rect.h)):
                is_move = True
                print("[{}] =按下鼠標 開關變量:True=".format(i))

            else:
                print("[{}] <按下鼠標 開關變量:False>".format(i))

        # 松開鼠標表示無法拖拽了
        if event.type == pygame.MOUSEBUTTONUP:  # 松開鼠標
            print("[{}] |松開鼠標 開關變量:False|".format(i))
            is_move = False

        # 如果允許拖拽了 實際操作就在這個事件里面實現
        if event.type == pygame.MOUSEMOTION:
            if is_move:
                tmp_x, tmp_y = event.pos  # 接收當前鼠標坐標
                # 判斷下坐標是否合法 簡單說是否超出范圍 超出則歸位
                if tmp_x < 0:
                    me1_rect.x = 0
                if tmp_x > win_x - me1_rect.w:
                    me1_rect.x = win_x - me1_rect.w
                if tmp_y < 0:
                    me1_rect.y = 0
                if tmp_y > win_y - me1_rect.h:
                    me1_rect.y = win_y - me1_rect.h

                # 保證鼠標處於圖像的中心位置
                me1_rect.x = tmp_x - int(me1_rect.w / 2)
                me1_rect.y = tmp_y - int(me1_rect.h / 2)
                print("[{}] ==鼠標移動 開關變量:True 坐標({}, {})==".format(i, me1_rect.x, me1_rect.y))

            else:
                print("[{}] 鼠標移動 False".format(i))

    # 移動本機 在事件里面調整要變動的圖像坐標  在這里描繪出來
    print("[{}] 移動本機 坐標({}, {})".format(i, me1_rect.x, me1_rect.y))
    windows1.blit(me1_surface, me1_rect)
    pygame.display.update()  # 刷新界面


    i += 1

pygame.quit()  # 退出


常用鍵盤事件

[<Event(2-KeyDown {'unicode': '', 'key': 115, 'mod': 0, 'scancode': 31, 'window': None})>]

[<Event(3-KeyUp {'key': 115, 'mod': 0, 'scancode': 31, 'window': None})>]


鍵盤事件的3個常用例子

1.檢測鍵盤按鍵 實現上下左右的移動

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# author:albert time:2020/9/17
import pygame

# import os
pygame.init()  # 初始化

# 參數配置
win_w = 480  # 游戲窗口大小
win_h = 700
FPS = 60  # FPS常量

# 需要的圖片路徑
me1_path = "images/me1.png"  # 我方飛機圖的路徑
enemy1_path = "images/enemy1.png"  # 敵機圖的路徑
background_path = "images/background.png"  # 背景圖的路徑

# 把需要用到的圖片都載入進來作為Surface對象 備用
me1_surface = pygame.image.load(me1_path)
enemy1_surface = pygame.image.load(enemy1_path)
background_surface = pygame.image.load(background_path)

# 獲取每個圖像的Rect對象 以便后面控制圖像變動位置等等
me1_rect = me1_surface.get_rect()
enemy1_rect = enemy1_surface.get_rect()
background_rect = background_surface.get_rect()

# 設置各個對象的初始坐標 從Surface對象那邊獲取的寬高是對的 但是坐標是0,0 所以這里要設置我機的初始坐標
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))
me1_rect.x = int((win_w - me1_rect.w) / 2)  # 我機起始在最下面那行的中間位置開始
me1_rect.y = win_h - me1_rect.h
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))

# 創建游戲窗口 本質還是創建圖像或者surface對象
windows1 = pygame.display.set_mode((win_w, win_h), 0, 32)

# 一開始的游戲界面繪圖
windows1.blit(background_surface, (0, 0))  # 描繪背景
windows1.blit(me1_surface, me1_rect)  # 描繪我機
pygame.display.update()  # 刷新窗口界面


def check_pic_position(rect, tmp_win_w, tmp_win_h):
    """
    funtion:為了處理窗口內的圖像不能超出窗口范圍 超出了則回到窗口內
    :param rect: 圖像的rect對象
    :param tmp_win_w: 窗口的寬
    :param tmp_win_h: 窗口的高
    :return:返回一個rect對象
    """
    if rect.x < 0:
        rect.x = 0
    if rect.x > tmp_win_w - rect.w:
        rect.x = tmp_win_w - rect.w
    if rect.y < 0:
        rect.y = 0
    if rect.y > tmp_win_h - rect.h:
        rect.y = tmp_win_h - rect.h
    return rect


# 游戲流程開始
clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0  # 區別每個循環的計數君

while True:

    clock.tick(FPS)  # fps

    # 描繪背景
    windows1.blit(background_surface, (0, 0))
    # print(pygame.event.get())
    # 開始遍歷事件做針對處理 同時避免卡頓
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

        if event.type == pygame.KEYDOWN:

            if event.key == pygame.K_UP:  # 判斷按鍵可以使用pygame的按鍵常量 也可以使用按鍵碼
            # if event.key == 273:

                print("向上")
                me1_rect.y -= 10
            elif event.key == 274:
                print("向下")
                me1_rect.y += 10
            elif event.key == 276:
                print("向左")
                me1_rect.x -= 10
            elif event.key == 275:
                print("向右")
                me1_rect.x += 10
            else:
                print(event.key)

    # 描繪背景
    windows1.blit(background_surface, (0, 0))

    # 保證rect的坐標變動不會超出窗口范圍 用一個獨立函數來處理
    me1_rect = check_pic_position(me1_rect, win_w, win_h)

    # 移動本機
    windows1.blit(me1_surface, me1_rect)
    pygame.display.update()  # 刷新界面

    # print("[{}]:耗費{}毫秒,飛機坐標為({}, {})".format(i, clock.get_time(), me1_rect.x, me1_rect.y))

    i += 1

pygame.quit()  # 退出

按鍵碼 https://www.jianshu.com/p/ffefa2af6d47

問題是 長按上下左右效果無法實現 只能點擊松開點擊松開

2.按鍵一直按下的實現

使用pygame.key.get_pressed()實現的 上下左右的移動 很平滑 可以一直按下去 注意的是 pygame.key.get_pressed() 不要寫在pygame.event.get()里面 很容易出現閃退的情況   pygame.key.get_pressed() 和 pygame.event.get()是平級的

# 游戲流程開始
clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0  # 區別每個循環的計數君

while True:

    clock.tick(FPS)  # fps

    # 描繪背景
    windows1.blit(background_surface, (0, 0))
    # 使用 pygame.key.get_pressed 來控制上下左右 可以按住移動 很平滑
    keys = pygame.key.get_pressed()
    mods = pygame.key.get_mods()

    if keys[pygame.K_UP]:
        print("向上")
        me1_rect.y -= 10
    elif keys[pygame.K_DOWN]:
        print("向下")
        me1_rect.y += 10
    elif keys[pygame.K_LEFT]:
        print("向左")
        me1_rect.x -= 10
    elif keys[pygame.K_RIGHT]:
        print("向右")
        me1_rect.x += 10
    # 開始遍歷事件做針對處理 同時避免卡頓
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # 描繪背景
    windows1.blit(background_surface, (0, 0))

    # 保證rect的坐標變動不會超出窗口范圍 用一個獨立函數來處理
    me1_rect = check_pic_position(me1_rect, win_w, win_h)

    # 移動本機
    windows1.blit(me1_surface, me1_rect)
    pygame.display.update()  # 刷新界面

    # print("[{}]:耗費{}毫秒,飛機坐標為({}, {})".format(i, clock.get_time(), me1_rect.x, me1_rect.y))

    i += 1

3 組合鍵 不知道為啥 查了資料說是要使用 pygame.key.get_mods()配合使用 但是 實際測試中只需要 pygame.key.get_pressed() 就夠了 足矣實現組合鍵 但是注意按鍵常量寫法和get_mods() 有變化

keys = pygame.key.get_pressed()
    mods = pygame.key.get_mods()

    if keys[pygame.K_UP]:
        print("向上")
        me1_rect.y -= 10
    elif keys[pygame.K_DOWN]:
        print("向下")
        me1_rect.y += 10
    elif keys[pygame.K_LEFT]:
        print("向左")
        me1_rect.x -= 10
    elif keys[pygame.K_RIGHT]:
        print("向右")
        me1_rect.x += 10
    elif keys[pygame.K_F4] and keys[pygame.K_LALT]:  # 點擊alt+f4 表示關閉
        print("即將關閉")
        pygame.quit()

窗口未響應問題(窗口卡頓嚴重 出現未響應標題)

原因

pygame中必須在循環體中對pygame.event.get()做出響應,不然系統就會認為窗口沒有響應,鼠標就會一直轉等待響應,而且點了以后,窗口會顯示無響應

解決辦法
在while循環中加上對事件的響應即可

無限循環加入此代碼 即可解決

# 無限循環留下一個退出的通道
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()


創建主窗口和繪圖

創建窗口(創建surface對象 或者創建圖像)

# 參數配置
win_x = 480  # 游戲窗口大小
win_y = 700
FPS = 50  # FPS常量

# 需要的圖片路徑
background_path = "images/background.png"  # 背景圖的路徑

# 把需要用到的圖片都載入進來作為Surface對象 備用
background_surface = pygame.image.load(background_path)

# 獲取每個圖像的Rect對象 以便后面控制圖像變動位置等等
background_rect = background_surface.get_rect()


# 創建游戲窗口 本質還是創建圖像或者surface對象
windows1 = pygame.display.set_mode((win_x, win_y), 0, 32)

# 游戲界面繪圖
windows1.blit(background_surface, (0, 0))  # 描繪背景
pygame.display.update()  # 刷新窗口界面


創建窗口 繪圖 和 事件觸發 移動圖像的整體例子

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# author:albert time:2020/9/17
import pygame
# import os
pygame.init()  # 初始化

# 參數配置
win_x = 480  # 游戲窗口大小
win_y = 700
FPS = 50  # FPS常量

# 需要的圖片路徑
me1_path = "images/me1.png"  # 我方飛機圖的路徑
enemy1_path = "images/enemy1.png"  # 敵機圖的路徑
background_path = "images/background.png"  # 背景圖的路徑

# 把需要用到的圖片都載入進來作為Surface對象 備用
me1_surface = pygame.image.load(me1_path)
enemy1_surface = pygame.image.load(enemy1_path)
background_surface = pygame.image.load(background_path)

# 獲取每個圖像的Rect對象 以便后面控制圖像變動位置等等
me1_rect = me1_surface.get_rect()
enemy1_rect = enemy1_surface.get_rect()
background_rect = background_surface.get_rect()

# 設置各個對象的初始坐標 從Surface對象那邊獲取的寬高是對的 但是坐標是0,0 所以這里要設置我機的初始坐標
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))
me1_rect.x = int((win_x - me1_rect.w)/2)  # 我機起始在最下面那行的中間位置開始
me1_rect.y = win_y - me1_rect.h
print("x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))

# 創建游戲窗口 本質還是創建圖像或者surface對象
windows1 = pygame.display.set_mode((win_x, win_y), 0, 32)

# 一開始的游戲界面繪圖
windows1.blit(background_surface, (0, 0))  # 描繪背景
windows1.blit(me1_surface, me1_rect)  # 描繪我機
pygame.display.update()  # 刷新窗口界面

# 游戲流程開始
clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0
while True:

    clock.tick(FPS)  # 每次執行到這里就會自動重置

    # 無限循環留下一個退出的通道
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # 描繪背景
    windows1.blit(background_surface, (0, 0))
    # 計算我機本次的移動位置 到了最上面就自動回到原點重新跑
    me1_rect.y -= 1
    if me1_rect.y + me1_rect.h <= 0:
        me1_rect.y = win_y - me1_rect.h
    # 移動本機
    windows1.blit(me1_surface, me1_rect)
    pygame.display.update()  # 刷新界面

    print("[{}]:耗費{}毫秒,飛機坐標為({}, {})".format(i, clock.get_time(), me1_rect.x, me1_rect.y))
    i += 1

pygame.quit()  # 退出


殘影問題的處理

我們在循環移動圖像的時候 有的時候會沿着移動路徑出現一連串的殘影 這個殘影是怎么發生的呢

image

代碼部分:

clock = pygame.time.Clock()  # 聲明一個時鍾對象
i = 0
while True:

    clock.tick(FPS)  # 每次執行到這里就會自動重置

    # 無限循環留下一個退出的通道 避免無限循環卡頓
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # 描繪背景
    # windows1.blit(background_surface, (0, 0))
    # 計算我機本次的移動位置 到了最上面就自動回到原點重新跑
    me1_rect.y -= 50
    if me1_rect.y + me1_rect.h <= 0:
        me1_rect.y = win_y - me1_rect.h
    # 移動本機
    windows1.blit(me1_surface, me1_rect)
    pygame.display.update()  # 刷新界面

    print("[{}]:耗費{}毫秒,飛機坐標為({}, {})".format(i, clock.get_time(), me1_rect.x, me1_rect.y))
    i += 1
問題:代碼上看 每次循環 都會描繪一次移動后的飛機圖像 上一次循環描繪的飛機圖像並不是隨着這一次的描繪就消失了而是依然存在窗口上 所有才導致這個問題

解決辦法:每次循環 先優先描繪一次背景圖 讓背景圖把上一次循環描繪的東西全部覆蓋掉 然后在新的背景上描繪本次循環要描繪的所有圖片對象


精靈和精靈組

https://blog.csdn.net/Enderman_xiaohei/article/details/88218773

精靈的作用:方便快捷的批量描述游戲對象 

比如我們之前游戲窗口有個飛機在移動 先是image.load加載該圖片對象 然后獲取該對象的rect 然后再循環的鋪背景和控制rect來實現該圖片對象的變動 實現了這個圖像對象的動畫效果 但是呢 如果有上百個類似的圖像都要在這個游戲窗口運動怎么辦 大批量的處理這些圖像對象就可以吧每個圖像對象都設置為一個精靈子類的實例 然后這些精靈子類實例分門別類的歸到各個精靈組里面 每個精靈組內的精靈可以統一的協調的運動 並且里面封裝了一批游戲對象常用的方法 比如碰撞測試

精靈組的作用:把一系列的游戲對象歸到一個或者幾個組 讓他們統一操作


如何使用精靈和精靈租

1.根據你的實際需要來構建精靈子類 重寫update方法來描述該子類的實例對象的具體行為

繼承精靈的子類需要注意的地方:

  • 要在子類的__init__里面再次調用父類的初始化方法
  • 需要配置類實例屬性Sprite.image Sprite.rect 一般也都是在__init__里面定義好的
  • 重寫update方法 因為在父類里面這個方法其實是個空方法 當前這個子類的實例對象想要實現什么操作  比如上下有規則移動 都可以在這個類實例方法中描述
class EnemyClass(pygame.sprite.Sprite):
    """
    為敵人飛機提供精靈子類
    """
    # 按照規范要在sprite的子類的__init__里面載入pygame.sprite.Sprite的init方法
    # 並且要在這個sprite的子類的__init__里面給類實例屬性image和rectify賦初值
    def __init__(self, image_path, start_x=0, start_y=0):
        pygame.sprite.Sprite.__init__(self)  # 載入pygame.sprite.Sprite的init方法
        # 類實例屬性image和rectify賦初值
        self.image = pygame.image.load(image_path)
        self.rect = self.image.get_rect()
        self.rect.x = start_x  # 修正初始坐標 從get_rect獲取的坐標默認是0 0
        self.rect.y = start_y
        print("敵人實例x:{},y:{},w:{},h:{}".format(me1_rect.x, me1_rect.y, me1_rect.w, me1_rect.h))

    # update是繼承自精靈父類的 但是精靈父類里面是個空殼子 需要我們重寫update方法來賦值這個精靈子類的行為模式
    def update(self, *args):
        print("敵人實例的update觸發")
        self.rect.y += 10
        # 到底了就從上面重新開始
        if self.rect.y >= win_h - self.rect.h:
            self.rect.y = 0

2.添加精靈子類實例進精靈組

enemy_group = pygame.sprite.Group()  # 定義一個敵人精靈組用來存儲 敵人類的實例
enemy_1 = EnemyClass(enemy1_path, 0, 100)  # 實例化一個敵人
enemy_2 = EnemyClass(enemy1_path, 200, 100)  # 實例化一個敵人
enemy_group.add(enemy_1, enemy_2)  # 把多個敵人實例添加到敵人精靈組里面

3 精靈組統一控制精靈

# 游戲流程開始
clock = pygame.time.Clock()  # 聲明一個時鍾對象
enemy_group = pygame.sprite.Group()  # 定義一個敵人精靈組用來存儲 敵人類的實例
enemy_1 = EnemyClass(enemy1_path, 0, 100)  # 實例化一個敵人
enemy_2 = EnemyClass(enemy1_path, 200, 100)  # 實例化一個敵人
enemy_group.add(enemy_1, enemy_2)  # 把多個敵人實例添加到敵人精靈組里面

i = 0  # 區別每個循環的計數君

while True:

    clock.tick(FPS)  # fps

    enemy1_rect.y += 10  # 敵人的飛機移動

    # 開始遍歷事件做針對處理 同時避免卡頓
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # 敵人
    enemy_group.update()  # 精靈組來統一觸發組內所有精靈的update方法
    enemy_group.draw(windows1)  # 精靈組統一把組內精靈描繪到游戲窗口上

    pygame.display.update()  # 刷新界面

    i += 1

4 從精靈組刪除精靈

精靈組.remove(精靈名)  沒什么可說的

5 精靈 精靈組之間檢測碰撞


精靈與精靈之間的碰撞檢測
pygame.sprite.collide_rect(left, right) -> bool
測試兩個精靈之間的碰撞。 使用pygame rect colliderect函數計算碰撞。 作為碰撞回調函數傳遞給*collide函數。 精靈必須具有“rect”屬性。

精靈與精靈組的碰撞檢測
pygame.sprite.spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
返回一個列表,其中包含與另一個Sprite相交的Group中的所有Sprite。 通過比較每個Sprite的Sprite.rect屬性來確定交集。
dokill參數是一個布爾值。 如果設置為True,則將從組中刪除所有碰撞的Sprite。
碰撞參數是一個回調函數,用於計算兩個精靈是否發生碰撞。 它應該將兩個精靈作為值,並返回一個bool值,指示它們是否發生碰撞。 如果未傳遞碰撞,則所有精靈必須具有“rect”值,該值是sprite區域的矩形,將用於計算碰撞。

兩個精靈組之間發生碰撞
pygame.sprite.groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
這將在兩組中找到所有精靈之間的碰撞。 通過比較每個Sprite的Sprite.rect屬性或使用碰撞函數(如果它不是None)來確定碰撞。
group1中的每個Sprite都被添加到返回字典中。 每個項的值是group2中相交的Sprite列表。
如果dokill參數為True,則將從各自的組中刪除碰撞的Sprite。
碰撞參數是一個回調函數,用於計算兩個精靈是否發生碰撞。 它應該將兩個精靈作為值並返回一個bool值,指示它們是否發生碰撞。 如果未傳遞碰撞,則所有精靈必須具有“rect”值,該值是精靈區域的矩形,將用於計算碰撞。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM