2. pygame事件

pygame之事件

什麼是事件

和事件關聯的動詞,是「發生」,因此當咱們在關注事件的時候,咱們其實就是在關注當前正在發生什麼……
舉一個很是扯淡的例子,在咱們口語表達中,有這樣一種嫌棄:「你事情怎麼這麼多的?」。這是一種南方的口語表達,可能還不是很是明顯,換成北方的表達方式,就很直接了:「你怎麼這麼事兒?」
這個「事兒」咱們就能夠理解爲,操做。咱們的程序是會一直一直運行下去的,直到我關閉窗口的操做產生了一個QUIT事件。事件隨時可能發生,並且量也可能會很大,pygame的作法是把一系列的事情存放在一個隊列裏,逐個處理。python

事件檢索

一般,在我目前寫到的程序中,我都使用pygame.event.get()來處理全部的事件。若是咱們使用pygame.event.wait(),pygame就會等到發生下一個事件才繼續下去。
在一些動態遊戲中,遊戲每每是要動態運做的,而另一個方法pygame.event.poll()就好一些,一旦調用,他會根據如今的情形返回一個真實的事件,或者一個「什麼都沒有」。app

下表爲一個經常使用事件集:post

事件 產生途徑 參數
QUIT 用戶按下關閉按鈕 none
ATIVEEVENT pygame被激活或者隱藏 gain,state
KEYDOWN 鍵盤被按下 unicode,key,mod
KEYUP 鍵盤被放開 key,mod
MOUSEMOTION 鼠標移動 pos,rel,buttons
MOUSEBUTTONDOWN 鼠標按下 pos,button
MOUSEBUTTONUP 鼠標放開 pos,button
JOYAXISMOTION 遊戲手柄(joystick or pad)移動 joy,axis,value
JOYBALLMOTION 遊戲手柄(joystick ball)移動 joy,axis,value
JOYHATMOTION 遊戲手柄(joystick)移動 joy,axis,value
JOYBUTTONDOWN 遊戲手柄按下 joy,button
JOYBUTTONUP 遊戲手柄放開 joy,button
VIDEORESIZE pygame窗口縮放 size,w,h
VIDEOEXPOSE pygame窗口部分公開(expose) none
USEREVENT 觸發用戶事件 code

一個用來測試查看事件的小腳本

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen_size = (640, 480)
screen = pygame.display.set_mode(screen_size, 0, 32)

font = pygame.font.SysFont("arial", 17)
font_height = font.get_linesize()
event_text = []

while 1:
    event = pygame.event.wait()
    event_text.append(str(event))
    event_text = event_text[(-screen_size[1]//font_height):]
    # 這個切片操做保證了event_text裏面只保留一個屏幕的文字

    if event.type == QUIT:
        exit()
    screen.fill((0, 0, 0))
    # screen.blit()
    y = screen_size[1]-font_height
    # 找一個合適的起筆位置,最下面開始可是要留一行空

    for text in reversed(event_text):
        screen.blit(font.render(text,True,(0,255,0)),(0,y))
        y -= font_height

    pygame.display.update()

這個程序很是適合分步去了解各個操做在pygame內的響應效果,可是有一個小小的弊端就是,他和通常的pygame.event.get()不一樣,它只有在新的事件發生的時候纔會有反饋到屏幕上,這就讓咱們形成某種錯覺,就是pygame只能知道咱們某瞬間的狀態,若是這個狀態不改變,pygame就沒法察覺。
顯然這是不對的,例如咱們按下某個鍵的時候,咱們能夠用for語句搭配pygame.event.get去進行一個輪詢,這個輪詢會不斷的檢測事件,pygame始終能覺察到咱們的操做和狀態。
下面的程序就是一個典型的例子。在長按方向鍵的時候,pygame並不會移動一次就結束,而是會一直移動,直到咱們鬆開按鍵返回原始狀態。測試

background_image_filename = 'sushiplate.jpg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

x, y = 0, 0
move_x, move_y = 0, 0

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
           exit()
        if event.type == KEYDOWN:
            #鍵盤有按下?
            if event.key == K_LEFT:
                #按下的是左方向鍵的話,把x座標減一
                move_x = -1
            elif event.key == K_RIGHT:
                #右方向鍵則加一
                move_x = 1
            elif event.key == K_UP:
                #相似了
                move_y = -1
            elif event.key == K_DOWN:
                move_y = 1
        elif event.type == KEYUP:
            #若是用戶放開了鍵盤,圖就不要動了
            move_x = 0
            move_y = 0

    #計算出新的座標
    x+= move_x
    y+= move_y

    screen.fill((0,0,0))
    screen.blit(background, (x,y))
    #在新的位置上畫圖
    pygame.display.update()

舉個經常使用的栗子

經過上面的動做檢測腳本咱們能夠發現,出現頻率最高的,就是鼠標事件和鍵盤事件。因此在這裏咱們特別地來討論一下這兩個事件的典型用法。code

處理鼠標事件

MOUSEMOTION事件會在鼠標動做的時候發生,他有三個參數:隊列

  • buttons:一個含有三個數字的元組,三個值分別表示左鍵、中鍵、右鍵。1表示按下,0表示空放狀態。
  • pos:position,表示位置
  • rel:表明當前座標距離上次鼠標事件的距離,表示形式也是一個二維元組。

MOUSEBUTTONDOWNMOUSEBUTTONUP遊戲

  • button:這個參數,跟上面比,少了一個s,這個表明了那個按鍵被操做
  • pos:仍是位置

處理鍵盤事件

KEYDOWNKEYUP的參數描述:事件

  • key:按下或者放開的鍵值,是一個數字,估計地球上不多有人能夠徹底記得住,因此pygame中你可使用K_xxx來表示,好比字母a就是K_a,還有K_SPACE和K_RETURN等。
  • mod:包含了組合鍵信息,若是mod&KMOD_CTRL的值爲真,表示用戶同時按下了ctrl鍵。相似的還有KMOD_SHIFT,KMOD_ALT
  • unicode:表明了按下鍵的unicode值

案例在上面已經放了,就是經過方向鍵移動圖片的小腳本。可是這個腳本有一個小小的bug:「在快速切換方向時,有時候會出現沒法持續移動的狀況」,這種bug的緣由其實跟pygame.event.get()機制有關。event.get只能監聽接收一個事件,在咱們快速操做的時候,好比咱們按下左鍵,忽然按下上鍵而且鬆開左鍵,這種時候bug就會出現。
咱們把上述動做分解一下:圖片

  • KEYDOWN-------K_LEFT
  • KEYDOWN-------K_UP
  • KEYUP-----------K_LEFT

顯而易見,最後的終結操做並非KEYDOWN,而是KEYUP,在咱們的代碼中KEYUP會把移動位給歸零。這時候即使K_UP仍是在一個KEYDOWN的狀態,可是他再也不被event.get到了,因此天然,這個圖像就停下來了。ip

改良版:

background_image_filename = 'sushiplate.jpg'

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()

x, y = 0, 0
move_x, move_y = 0, 0

move={K_LEFT:0,K_RIGHT:0,K_UP:0,K_DOWN:0}


while True:
    for event in pygame.event.get():
        if event.type == QUIT:
           exit()
        if event.type == KEYDOWN:
            #鍵盤有按下?
            if event.key in move.keys():
                #按下的是左方向鍵的話,把x座標減一
                move[event.key]=1

        elif event.type == KEYUP:
            if event.key in move.keys():
                move[event.key]=0
    #計算出新的座標
    x-= move[K_LEFT]
    x+= move[K_RIGHT]
    y-= move[K_UP]
    y+= move[K_DOWN]


    screen.fill((0,0,0))
    screen.blit(background, (x,y))
    #在新的位置上畫圖
    pygame.display.update()

事件過濾

並非全部的事件都是須要處理的,好比在遊戲場景切換的時候,你按什麼都沒有用。咱們應該有一個方法來過濾掉一些咱們不感興趣的事件(固然咱們能夠不處理,不給他們設置響應的事件,可是最好的方法仍是讓他們根本不進入咱們的事件隊列)。
咱們使用pygame.event.set_blocked(事件名)來完成。若是有好多事件須要過濾,能夠傳遞一個列表pygame.event.set_blocked([list]),若是是設置參數爲None,那麼全部的事件就被打開了。
與之相對的,咱們使用pygame.event.set_allowed()來設定容許的事件

產生事件

一般玩家作什麼,pygame產生對應的事件就能夠了,不過有的時候咱們須要模擬出一些事件來,好比錄像回放的時候,咱們就要把用戶的操做再現一遍。或者說咱們在作外掛的時候,咱們就要把一些用戶的操做自動完成。

my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=u' ')
#你也能夠像下面這樣寫,看起來比較清晰(但字變多了……)
my_event = pygame.event.Event(KEYDOWN, {"key":K_SPACE, "mod":0, "unicode":u' '})
pygame.event.post(my_event)

有時候咱們甚至能夠產生一個徹底自定義的全新事件。範例代碼以下,可是我本身並無敲成功,由於我不知道它的這個USEREVENT在哪裏何時如何定義的

CATONKEYBOARD = USEREVENT+1
my_event = pygame.event.Event(CATONKEYBOARD, message="Bad cat!")
pgame.event.post(my_event)

#而後得到它
for event in pygame.event.get():
    if event.type == CATONKEYBOARD:
        print event.message
相關文章
相關標籤/搜索