第十八章 一種新的輸入——事件
事件(event)其實(shí)就是一種特殊的輸入~通過它,Python可以知道具體發(fā)生了什么事情,并由此展開一系列行動(dòng)。
18.1 事件
事件到底是什么呢?我們舉些例子來(lái)理解:
(1)移動(dòng)或點(diǎn)擊鼠標(biāo);
(2)按鍵;
(3)經(jīng)過某段時(shí)間
這些都是事件。他們可以作為一種特殊的輸入依據(jù),假如滿足了特定的時(shí)間,就會(huì)觸發(fā)事件驅(qū)動(dòng)程序(event-driven program)。Windows操作系統(tǒng)(或其他GUI)就是很好的例子:打開計(jì)算機(jī)后,你不動(dòng),計(jì)算機(jī)是不可能自己進(jìn)行操作的;但當(dāng)你移動(dòng)鼠標(biāo)、敲擊鍵盤或等待足夠長(zhǎng)時(shí)間,它就會(huì)有相應(yīng)的反應(yīng)(最后一個(gè)會(huì)啟動(dòng)屏幕保護(hù)程序)。
我們?cè)倏纯匆韵赂拍睿?/p>
(1)事件循環(huán)(event loop):不斷尋找時(shí)間的特殊循環(huán)叫做事件循環(huán),通常是最后的那個(gè)while循環(huán)。
(2)事件隊(duì)列(event queue):內(nèi)存中存儲(chǔ)事件的部分叫做事件隊(duì)列,它是個(gè)列表,用來(lái)存儲(chǔ)要發(fā)生的所有事件。
(3)事件處理器(event handler):事件有很多,如鼠標(biāo)可以產(chǎn)生mouse_move和mouse_click等事件。并不是每個(gè)事件都值得處理,所以程序中特別開辟出處理某個(gè)事件的部分,稱為事件處理器(如專門處理mouse_click的處理器)。
18.2 鍵盤事件
在Pygame中,使用KEYDOWN告訴電腦“我想按下鍵盤上的鍵就觸發(fā)其他事情發(fā)生”?,F(xiàn)在看下面一個(gè)例子:

一些常用的鍵盤事件:
KEYDOWN
KEYUP
可以在這里查看更多的pygame鍵盤事件。
18.3 鼠標(biāo)事件
跟上面鍵盤事件一樣,這里同樣在elif中可以使用以下鼠標(biāo)事件:
MOUSEMOTION : 鼠標(biāo)移動(dòng)
MOUSEBUTTONDOWN : 按下鼠標(biāo)
MOUSEBUTTONUP : 松開鼠標(biāo)
18.4 定時(shí)器事件
使用定時(shí)器事件,我們可以自定義事件以及它的生效時(shí)間(就像鬧鐘):
pygame.time.set_timer(EVENT_NUMBER, interval)
其中的EVENT_NUMBER代表用戶設(shè)置的事件編號(hào)(就像我玩“富甲天下”時(shí)用到的錦囊,在游戲里是用編號(hào)來(lái)表示的),用戶可以從24~32中選一個(gè)數(shù)字來(lái)代表;不過最好還是先用pygame.USEREVENT來(lái)代替免得出錯(cuò)。

18.5 另一個(gè)游戲——PyPongs
將前面所學(xué)的整合起來(lái),咱們來(lái)玩新的游戲——PyPongs!


這是一個(gè)不完整的游戲,接下來(lái)還會(huì)繼續(xù)補(bǔ)充~
記錄分?jǐn)?shù),并用pygame.font顯示
現(xiàn)在我們來(lái)補(bǔ)充游戲:
(1)建立font對(duì)象,告訴Pygame你想要的字體樣式和大小
(2)渲染文本,向字體對(duì)象傳入一個(gè)字符串,它會(huì)返回一個(gè)繪制這個(gè)文本的表面
(3)把這個(gè)表面移到顯示面
我們需要在paddle = MyPaddleClass([270, 400])后面加上:
# 創(chuàng)建字體對(duì)象
score_font = pygame.font.Font(None, 50)
# 渲染文本到表面surf,后面記得將int改為str
score_surf = score_font.render(str(score), 1, (0,0,0))
# 設(shè)置文本位置
score_pos = [10,10]
然后,因?yàn)樵谟螒蜻\(yùn)行的過程中,分?jǐn)?shù)需要不斷改變,所以在事件循環(huán)內(nèi)部,我們需要這行代碼,把含有分?jǐn)?shù)文本的那個(gè)表面移到最上面來(lái):
screen.blit(score_surf, score_pos)?
并且在font前面創(chuàng)建一個(gè)score = 0的變量,這樣就不會(huì)報(bào)錯(cuò)了。
跟蹤分?jǐn)?shù)
if self.rect.top <= 0 :
? ? self.speed[1] = - self.speed[1]
? ? score = score + 1
? ? score_surf = socre_font.render(str(score), 1, (0,0,0))
其實(shí)就是將上面的代碼加到if檢測(cè)中;但這樣運(yùn)行后,會(huì)有命名空間的報(bào)錯(cuò),這時(shí)候我們需要重新給score、score_font還有socre_surf這三個(gè)變量進(jìn)行全局命名:
def move(self):
? ? global score, score_font, score_surf
跟蹤還有幾條命
首先我們需要建立一個(gè)生命對(duì)象lives = 3。
然后設(shè)計(jì):玩家漏了球后,lives減1,等待數(shù)秒,然后提供一個(gè)新球:
if myBall.rect.top >= screen.get_rect().bottom:
? ? lives = lives - 1
? ? pygame.time.delay(2000)
? ? myBall.rect.topleft = [50,50]
增加一個(gè)生命計(jì)數(shù)器
for i in range(lives):?
? ? width = screen.get_rect.width
? ? screen.blit(myBall.image, [width - 40 * i, 20])
思考一下,這個(gè)代碼要放在那兒?
因?yàn)樗M(jìn)行循環(huán)計(jì)數(shù)功能,所以要放在while循環(huán)中;但要在screen.blit代碼行后。
游戲結(jié)束
最后在游戲結(jié)束時(shí),我們要彈出消息提示,并終止再次產(chǎn)生球。
if myBall.rect.top >= screen.get_rect().bottom:
? ? lives = lives - 1
? ? if lives == 0 :
? ? ? ? final_text1 = "GAME OVER"
? ? ? ? final_text2 = "YOUR FINAL SCORE IS:????" + str(score)
? ? ? ? ft1_font = pygame.font.Font(None, 70)
# 居中文本
? ? ? ? ft1_surf = ft1_font.render(final_text1, 1, (0,0,0))?
? ? ? ? ft2_font = pygame.font.Font(None, 50)
? ? ? ? ft2_surf = ft2_font.render(final_text2, 1, (0,0,0))
? ? ? ? screen.blit(ft1_surf, [screen.get_width()/2 - \
? ? ? ? ? ? ? ? ? ? ? ? ? ? ft1_surf.get_width()/2, 100])
? ? ? ? screen.blit(ft2_surf, [screen.get_width()/2 - \
? ? ? ? ? ? ? ? ? ? ? ? ? ? ft2_surf.get_width()/2, 100])
? ? ? ? pygame.display.flip()
? ? ? ? done = True
? ? else: # Wait 2 seconds, then start the next ball
? ? ? ? pygame.time.delay(2000)
? ? ? ? myBall.rect.topleft = [(screen.get_rect().width) - 40 * lives, 20]
最終的PyPong代碼



你學(xué)到了什么
在這一章,我們學(xué)到了以下內(nèi)容:
(1)事件
(2)Pygame事件循環(huán)
(3)事件處理
(4)鍵盤、鼠標(biāo)、定時(shí)器等事件
(5)pygame.font
小結(jié)
這一章也是中等燒腦的一章,不算太難,很多知識(shí)點(diǎn)都是以前學(xué)過的,滾雪球一樣來(lái)到我們面前,或許技能就是在這種一復(fù)一日地練習(xí)中掌握的吧。繼續(xù)~