上次我們試著寫了一個最簡單的Pygame程式並且解釋了一個大概的框架,這次就Pygame中也是遊戲中最關鍵(……好吧,也許並不是最關鍵,但絕對是至關重要的一項)的事件來展開。
此圖為一個用Pygame開發的遊戲,或許有些簡陋,但是隻要你有愛,什麼都能出來!
理解事件
事件是什麼,其實從名稱來看我們就能想到些什麼,而且你所想到的基本就是事件的真正意思了。我們上一個程式,會一直執行下去,直到你關閉視窗而產生了一個QUIT事件,Pygame會接受使用者的各種操作(比如按鍵盤,移動滑鼠等)產生事件。事件隨時可能發生,而且量也可能會很大,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 | 遊戲球(Joy 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 |
如果你想把這個表現在就背下來,當然我不會阻止你,但實在不是個好主意,在實際的使用中,自然而然的就會記住。我們先來寫一個可以把所有方法輸出的程式,它的結果是這樣的。
我們這裡使用了wait(),因為這個程式在有事件發生的時候動彈就可以了。還用了font模組來顯示文字(後面會講的),下面是原始碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
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", 16); font_height = font.get_linesize() event_text = [] while True: 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((255, 255, 255)) y = SCREEN_SIZE[1]-font_height #找一個合適的起筆位置,最下面開始但是要留一行的空 for text in reversed(event_text): screen.blit( font.render(text, True, (0, 0, 0)), (0, y) ) #以後會講 y-=font_height #把筆提一行 pygame.display.update() |
小貼士:
書上說,如果你把填充色的(0, 0, 0)改為(0, 255, 0),效果會想黑客帝國的字幕雨一樣,我得說,實際試一下並不太像……不過以後你完全可以寫一個以假亂真甚至更酷的!
這個程式在你移動滑鼠的時候產生了海量的資訊,讓我們知道了Pygame是多麼的繁忙……我們第一個程式那樣是呼叫pygame.mouse.get_pos()來得到當前滑鼠的位置,而現在利用事件可以直接獲得!
處理滑鼠事件
MOUSEMOTION事件會在滑鼠動作的時候發生,它有三個引數:
- buttons – 一個含有三個數字的元組,三個值分別代表左鍵、中鍵和右鍵,1就是按下了。
- pos – 就是位置了……
- rel – 代表了現在距離上次產生滑鼠事件時的距離
和MOUSEMOTION類似的,我們還有MOUSEBUTTONDOWN和MOUSEBUTTONUP兩個事件,看名字就明白是什麼意思了。很多時候,你只需要知道滑鼠點下就可以了,那就可以不用上面那個比較強大(也比較複雜)的事件了。它們的引數為:
- button – 看清楚少了個s,這個值代表了哪個按鍵被操作
- pos – 和上面一樣
處理鍵盤事件
鍵盤和遊戲手柄的事件比較類似,為KEYDOWN和KEYUP,下面有一個例子來演示使用方向鍵移動一些東西。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
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() |
當我們執行這個程式的時候,按下方向鍵就可以把背景圖移動,但是等等!為什麼我只能按一下動一下啊……太不好試了吧?!用腳掌考慮下就應該按著就一直動下去才是啊!?Pygame這麼垃圾麼……
哦,真是抱歉上面的程式碼有點小bug,但是真的很小,你都不需要更改程式碼本身,只要改一下縮排就可以了,你可以發現麼?Python本身是縮排編排來表現層次,有些時候可能會出現一點小麻煩,要我們自己注意才可以。
KEYDOWN和KEYUP的引數描述如下:
- key – 按下或者放開的鍵值,是一個數字,估計地球上很少有人可以記住,所以Pygame中你可以使用K_xxx來表示,比如字母a就是K_a,還有K_SPACE和K_RETURN等。
- mod – 包含了組合鍵資訊,如果mod & KMOD_CTRL是真的話,表示使用者同時按下了Ctrl鍵。類似的還有KMOD_SHIFT,KMOD_ALT。
- unicode – 代表了按下鍵的Unicode值,這個有點不好理解,真正說清楚又太麻煩,遊戲中也不太常用,說明暫時省略,什麼時候需要再講吧。
事件過濾
並不是所有的事件都需要處理的,就好像不是所有登門造訪的人都是我們歡迎的一樣。比如,俄羅斯方塊就無視你的滑鼠,而在遊戲場景切換的時候,你按什麼都是徒勞的。我們應該有一個方法來過濾掉一些我們不感興趣的事件(當然我們可以不處理這些沒興趣的事件,但最好的方法還是讓它們根本不進入我們的事件佇列,就好像在門上貼著“XXX免進”一樣),我們使用pygame.event.set_blocked(事件名)來完成。如果有好多事件需要過濾,可以傳遞一個列表,比如pygame.event.set_blocked([KEYDOWN, KEYUP]),如果你設定引數None,那麼所有的事件有被開啟了。與之相對的,我們使用pygame.event.set_allowed()來設定允許的事件。
產生事件
通常玩家做什麼,Pygame就產生對應的事件就可以了,不過有的時候我們需要模擬出一些事件來,比如錄影回放的時候,我們就要把使用者的操作再現一遍。
為了產生事件,必須先造一個出來,然後再傳遞它:
1 2 3 4 |
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) |
你甚至可以產生一個完全自定義的全新事件,有些高階的話題,暫時不詳細說,僅用程式碼演示一下:
1 2 3 4 5 6 7 8 |
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 |
這次的內容很多,又很重要,一遍看下來雲裡霧裡或者看的時候明白看完了全忘了什麼的估計很多,慢慢學習吧~~多看看動手寫寫,其實都很簡單。
下次講解顯示的部分。