2. pygame事件


pygame之事件

什么是事件

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

事件檢索

通常,在我目前寫到的程序中,我都使用pygame.event.get()來處理所有的事件。如果我們使用pygame.event.wait(),pygame就會等到發生下一個事件才繼續下去。
在一些動態游戲中,游戲往往是要動態運作的,而另外一個方法pygame.event.poll()就好一些,一旦調用,他會根據現在的情形返回一個真實的事件,或者一個“什么都沒有”。

下表為一個常用事件集:

事件 產生途徑 參數
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()

舉個常用的栗子

通過上面的動作檢測腳本我們可以發現,出現頻率最高的,就是鼠標事件和鍵盤事件。所以在這里我們特別地來討論一下這兩個事件的典型用法。

處理鼠標事件

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到了,所以自然,這個圖像就停下來了。

改良版:

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


免責聲明!

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



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