第十八章 一種新的輸入——事件
事件(event)其實就是一種特殊的輸入~通過它,Python可以知道具體發生了什么事情,并由此展開一系列行動。
18.1 事件
事件到底是什么呢?我們舉些例子來理解:
(1)移動或點擊鼠標;
(2)按鍵;
(3)經過某段時間
這些都是事件。他們可以作為一種特殊的輸入依據,假如滿足了特定的時間,就會觸發事件驅動程序(event-driven program)。Windows操作系統(或其他GUI)就是很好的例子:打開計算機后,你不動,計算機是不可能自己進行操作的;但當你移動鼠標、敲擊鍵盤或等待足夠長時間,它就會有相應的反應(最后一個會啟動屏幕保護程序)。
我們再看看以下概念:
(1)事件循環(event loop):不斷尋找時間的特殊循環叫做事件循環,通常是最后的那個while循環。
(2)事件隊列(event queue):內存中存儲事件的部分叫做事件隊列,它是個列表,用來存儲要發生的所有事件。
(3)事件處理器(event handler):事件有很多,如鼠標可以產生mouse_move和mouse_click等事件。并不是每個事件都值得處理,所以程序中特別開辟出處理某個事件的部分,稱為事件處理器(如專門處理mouse_click的處理器)。
18.2 鍵盤事件
在Pygame中,使用KEYDOWN告訴電腦“我想按下鍵盤上的鍵就觸發其他事情發生”?,F在看下面一個例子:
一些常用的鍵盤事件:
KEYDOWN
KEYUP
可以在這里查看更多的pygame鍵盤事件。
18.3 鼠標事件
跟上面鍵盤事件一樣,這里同樣在elif中可以使用以下鼠標事件:
MOUSEMOTION : 鼠標移動
MOUSEBUTTONDOWN : 按下鼠標
MOUSEBUTTONUP : 松開鼠標
18.4 定時器事件
使用定時器事件,我們可以自定義事件以及它的生效時間(就像鬧鐘):
pygame.time.set_timer(EVENT_NUMBER, interval)
其中的EVENT_NUMBER代表用戶設置的事件編號(就像我玩“富甲天下”時用到的錦囊,在游戲里是用編號來表示的),用戶可以從24~32中選一個數字來代表;不過最好還是先用pygame.USEREVENT來代替免得出錯。
18.5 另一個游戲——PyPongs
將前面所學的整合起來,咱們來玩新的游戲——PyPongs!
這是一個不完整的游戲,接下來還會繼續補充~
記錄分數,并用pygame.font顯示
現在我們來補充游戲:
(1)建立font對象,告訴Pygame你想要的字體樣式和大小
(2)渲染文本,向字體對象傳入一個字符串,它會返回一個繪制這個文本的表面
(3)把這個表面移到顯示面
我們需要在paddle = MyPaddleClass([270, 400])后面加上:
# 創建字體對象
score_font = pygame.font.Font(None, 50)
# 渲染文本到表面surf,后面記得將int改為str
score_surf = score_font.render(str(score), 1, (0,0,0))
# 設置文本位置
score_pos = [10,10]
然后,因為在游戲運行的過程中,分數需要不斷改變,所以在事件循環內部,我們需要這行代碼,把含有分數文本的那個表面移到最上面來:
screen.blit(score_surf, score_pos)?
并且在font前面創建一個score = 0的變量,這樣就不會報錯了。
跟蹤分數
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))
其實就是將上面的代碼加到if檢測中;但這樣運行后,會有命名空間的報錯,這時候我們需要重新給score、score_font還有socre_surf這三個變量進行全局命名:
def move(self):
? ? global score, score_font, score_surf
跟蹤還有幾條命
首先我們需要建立一個生命對象lives = 3。
然后設計:玩家漏了球后,lives減1,等待數秒,然后提供一個新球:
if myBall.rect.top >= screen.get_rect().bottom:
? ? lives = lives - 1
? ? pygame.time.delay(2000)
? ? myBall.rect.topleft = [50,50]
增加一個生命計數器
for i in range(lives):?
? ? width = screen.get_rect.width
? ? screen.blit(myBall.image, [width - 40 * i, 20])
思考一下,這個代碼要放在那兒?
因為它要進行循環計數功能,所以要放在while循環中;但要在screen.blit代碼行后。
游戲結束
最后在游戲結束時,我們要彈出消息提示,并終止再次產生球。
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代碼
你學到了什么
在這一章,我們學到了以下內容:
(1)事件
(2)Pygame事件循環
(3)事件處理
(4)鍵盤、鼠標、定時器等事件
(5)pygame.font
小結
這一章也是中等燒腦的一章,不算太難,很多知識點都是以前學過的,滾雪球一樣來到我們面前,或許技能就是在這種一復一日地練習中掌握的吧。繼續~