治療面對pygame的mouse.get_pressed()和MOUSEBUTTONDOWN時的選擇障礙

更新於 發佈於 閱讀時間約 10 分鐘

「小孩子才做選擇!」是句大家耳熟能詳的戲謔話,絕大多數的人在說這句話時,真正的實情是:全部都想要,但是沒能力全拿,有得選就偷笑了!

就是因為全部都想要,但是沒能力全拿,所以產生了選擇障礙這種症狀。其實,有個很簡單的辦法可以治療選擇障礙這個症狀:就丟銅板咩!反正自己下不了決心,那就讓老天爺幫忙決定不就好了?

雖然說一個銅板就能去除選擇障礙帶來的麻煩,但是寫程式時可不能這麼做。寫程式遇到有不同的東西都可以用來處理同一件事時,得先搞清楚,這些不同的東西之間到底有什麼差異?是不是在使用上有不同的限制?如果沒搞清楚就亂用一通,或許一時之間程式跑起來沒什麼不對勁,但那說不定是走了狗屎運,沒採到雷,等到處理的問題改變了,或者操作方式不一樣了,說不定就會出現完全想像不到的結果。最近在使用pygame偵測滑鼠動作的功能時,就充分體現了這種狀況。

原本是要寫一個模擬砲彈被大砲射出之後是如何運動的程式,因為覺得一執行程式就看到砲彈在空中飛,實在是有點太單調,所以就想著加入發砲的功能,也就是當按一下滑鼠左鍵時,就發射一顆砲彈。

這功能寫起來其實挺簡單的,只要能偵測到滑鼠左鍵有沒有被按下就可以了。查了一下,在pygame中,可以用mouse.get_pressed()或MOUSEBUTTONDOWN來偵測滑鼠是不是被按下,不過MOUSEBUTTONDOWN還牽涉到什麼event不event的,感覺用起來挺麻煩的。既然兩個都可以用來偵測滑鼠是不是被按下,那當然選用比較不麻煩的mouse.get_pressed()囉!

mouse.get_pressed()的用法很簡單,它會傳回一個tuple,裡頭放著三個boolean值,分別代表滑鼠左鍵、通常是滾輪的中間鍵、右鍵的狀態。如果按鍵是按下的,boolean值就是True,不然就是False。所以要偵測滑鼠左鍵是不是被按下,只要檢查mouse.get_press()[0]是不是True就可以了。

程式寫好之後,就開始測試看看效果如何。一開始還挺順的,一面點著滑鼠,一面看著砲彈滿天飛,還真是有點療癒。可是……怎麼覺得哪邊怪怪的。

咦?!為什麼同樣是按一下滑鼠,有時候會發射一顆砲彈,有時候卻會發射三顆砲彈?這可真是奇怪!明明程式是這樣寫的

while True:
    :
    if pygame.mouse.get_pressed()[0]:
        # 發射砲彈

看起來一點問題都沒有啊!那怎會這樣?如果這樣寫不行,那要怎樣寫才能按一下發射一顆砲彈?

爬文加上寫些小程式驗證自己的想法,總算搞清楚是怎麼一回事了。

寫小程式驗證想法,這個做法很重要,畢竟不管是網路上討論的文章或官網的文件,都不見得能面面俱到所有的小細節。有時候人家認為理所當然可以一句話帶過的觀念,卻是自己百思不得其解的大疑問。這時候,就只能靠自己寫程式,實際去驗證想法對不對了。

那為什麼會出現按一下發射三顆砲彈的情形呢?原來,這一切都因為mouse.get_pressed()傳回的是滑鼠按鈕的「狀態」,而不是「動作」。所以,只要呼叫mouse.get_pressed()時,滑鼠某個按鈕處於按下的狀態,對應於那個按鈕的傳回值,就會是True。因為從滑鼠按鈕按下到放開之間的時間雖然很短,但如果在這當中呼叫了不只一次mouse.get_pressed(),那就會被誤判為按了不只一次的滑鼠,導致出現明明只按了一次滑鼠,但卻發射了三顆砲彈的情況。

既然mouse.get_pressed()會有實際只按一次,但卻誤判為按了好幾次的情形,那豈不是沒什麼用處?畢竟那簡直就像是滑鼠用太久,已經快掛掉的症狀。其實,在玩射擊遊戲時,mouse.get_pressed()這樣的函數倒是挺好用的。滑鼠按著不放,子彈就砰砰砰砰砰連續發射,這時候巴不得發射越多越好,先前以為的缺點,這時候可不再是缺點,而是大大的優點啊!

mouse.get_pressed()在使用上,還有一個地方要注意的,那就是有可能滑鼠明明就按下又放開了,但卻沒偵測到。這也不難理解,這種情形會發生在滑鼠按鍵按下去再放開的過程中,mouse.get_pressed()根本就沒有被執行到。例如,當設定的fps不高或程式跑很慢時,有可能在滑鼠按鍵已經放開了之後,才執行到mouse.get_pressed(),這時候傳回來的滑鼠狀態,當然就是按鍵放開的狀態囉!

總之,mouse.get_pressed()就是個往事不需回首、過去了的就過去了,只活在當下,還有可能會恍神漏接的傢伙。

看來想要按一下滑鼠就只發射一顆砲彈,是不能用mouse.get_pressed()來處理了。那怎麼辦呢?沒有選擇之下,只好摸摸鼻子,用那個牽涉到什麼event不event的MOUSEBUTTONDOWN了。

什麼是event呢?翻譯成中文就是「事件」,就是某件發生的事情,例如滑鼠按鍵被按下或放開、滑鼠被移動、鍵盤被按下或放開等。在pygame中,定義了許多事件,如MOUSEBUTTONDOWN、MOUSEBUTTONUP、MOUSEMOTION、KEYDOWN、KEYUP、QUIT等,而使用者也可以自己定義需要的事件。

pygame利用事件佇列(event queue)來管理事件訊息的傳遞。每當有事件發生,例如滑鼠被按下時,就把這事件放到事件佇列裡頭去排隊等著處理,先排先處理。事件佇列有一定的容量,當放滿時,後續想要來排隊的事件,就會被默默地丟棄,彷彿從來不曾存在一般。

既然有事件發生時,就會被送入事件佇列中去等著執行,所以MOUSEBUTTONDOWN就不會像mouse.get_pressed()一樣,有漏接的情況出現。從這裡就可以看出來,MOUSEBUTTONDOWN的個性和mouse.get_pressed()是完全相反的。mouse.get_pressed()只活在當下,完全不管過去發生過什麼事;MOUSEBUTTONDOWN則會記得所有還沒處理完的事,只要事件佇列還沒被塞滿的話。

總之,跟mouse.get_pressed()的做法不同,MOUSEBUTTONDOWN偵測的是「動作」,也就是狀態的變化,而不是「狀態」。本來嘛,「事件發生」裡頭的「發生」這個詞,就是指「從沒有變有」,或「從有變沒有」,這種狀態的改變,不是嗎?

就因為MOUSEBUTTONDOWN偵測的是「動作」,而同一個滑鼠按鍵被按下這個動作,是不可能連續發生的,中間一定會有滑鼠按鍵被放開這個動作。所以,想要按一下滑鼠就只會發射一顆砲彈,MOUSEBUTTONDOWN就會是這種情況下的必然選擇。

治好了面對mouse.get_pressed()和MOUSEBUTTONDOWN時的選擇障礙後,接下來就是要搞清楚,使用MOUSEBUTTONDOWN時,要怎麼分辨被按下的,是滑鼠的哪個按鍵。

由pygame所定義的事件,都會帶有根據事件類型所設定的屬性,透過這些屬性,就可以知道事件更進一步的詳細資料。以MOUSEBUTTONDOWN來說,透過button這個屬性,就可以知道被按下的是哪個按鍵。當button裡頭放的值是1時,代表滑鼠左鍵被按下;是2時,代表按下的是中間鍵;是3時,則按下的是右鍵。

到這裡,總算搞清楚MOUSEBUTTONDOWN是怎麼運作的了。不過,要怎麼知道在事件佇列裡頭,有MOUSEBUTTONDOWN這個事件在排隊等著被處理?這個倒也不是太難辦到,只要利用event.get()把佇列裡頭的所有東西都拿出來,然後一個一個檢查,就可以知道了。

整合一下所有的東西,要想做到按一下滑鼠左鍵發射一顆砲彈的功能,程式可以這樣寫:

for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                # 發射一顆砲彈

最後,關於event.get(),有個細節應該要知道:event.get()是把在事件佇列裡頭的東西拿出來,不是複製出來,而且拿出來之後,不會再放回去。所以呼叫完event.get()之後,事件佇列是被清空的。先前提到過,事件佇列滿了之後,新的事件會被默默地丟棄。現在既然event.get()會把事件佇列清空,那就不需要去擔心這個問題了,因為通常event.get()會不斷地被呼叫,這樣才能知道是不是有新發生的事件需要處理,這也是為什麼使用pygame的程式,通常會看到下面的程式片段的原因:

while True:
    for event in pygame.event.get():
         if event.type == pygame.QUIT:
             :

發射砲彈的完整程式,可以在
《The Nature of Code閱讀心得筆記——使用Python實作》《The Nature of Code閱讀心得與Python實作》第3.2節Exercise 3.3中找到。

留言
avatar-img
留言分享你的想法!
avatar-img
ysf的沙龍
15會員
143內容數
寫點東西自娛娛人
ysf的沙龍的其他內容
2025/04/14
花了些時間,靜下心來,仔仔細細地研究了一番,總算把Python呼叫函數時引數的傳遞方式給徹底搞清楚了。
2025/04/14
花了些時間,靜下心來,仔仔細細地研究了一番,總算把Python呼叫函數時引數的傳遞方式給徹底搞清楚了。
2024/05/08
呼!折騰了好久,終於徹底搞清楚pygame的各個blend mode所用的計算式,到底是長啥樣子了。
2024/05/08
呼!折騰了好久,終於徹底搞清楚pygame的各個blend mode所用的計算式,到底是長啥樣子了。
2023/12/20
在寫《The Nature of Code閱讀心得筆記——使用Python實作》的[第四章]4.3節時,原書提到,在使用Java的ArrayList時,如果用迴圈一面走訪一面又移除其中的元素,那會有難以察覺的問題存在。寫個小程式測試的結果發現,Python的list也會有一樣的問題。
Thumbnail
2023/12/20
在寫《The Nature of Code閱讀心得筆記——使用Python實作》的[第四章]4.3節時,原書提到,在使用Java的ArrayList時,如果用迴圈一面走訪一面又移除其中的元素,那會有難以察覺的問題存在。寫個小程式測試的結果發現,Python的list也會有一樣的問題。
Thumbnail
看更多
你可能也想看
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
當你邊吃粽子邊看龍舟競賽直播的時候,可能會順道悼念一下2300多年前投江的屈原。但你知道端午節及其活動原先都與屈原毫無關係嗎?這是怎麼回事呢? 本文深入探討端午節設立初衷、粽子、龍舟競渡與屈原自沉四者。看完這篇文章,你就會對端午、粽子、龍舟和屈原的四角關係有新的認識喔。那就讓我們一起解開謎團吧!
Thumbnail
當你邊吃粽子邊看龍舟競賽直播的時候,可能會順道悼念一下2300多年前投江的屈原。但你知道端午節及其活動原先都與屈原毫無關係嗎?這是怎麼回事呢? 本文深入探討端午節設立初衷、粽子、龍舟競渡與屈原自沉四者。看完這篇文章,你就會對端午、粽子、龍舟和屈原的四角關係有新的認識喔。那就讓我們一起解開謎團吧!
Thumbnail
MOUSEBUTTONDOWN的個性和mouse.get_pressed()是完全相反的。mouse.get_pressed()只活在當下,完全不管過去發生過什麼事;MOUSEBUTTONDOWN則會記得所有還沒處理完的事,只要事件佇列還沒被塞滿的話。
Thumbnail
MOUSEBUTTONDOWN的個性和mouse.get_pressed()是完全相反的。mouse.get_pressed()只活在當下,完全不管過去發生過什麼事;MOUSEBUTTONDOWN則會記得所有還沒處理完的事,只要事件佇列還沒被塞滿的話。
Thumbnail
忽然覺得,似乎、彷彿、好像、應該有個for... else語法,可以讓程式漂亮一點。
Thumbnail
忽然覺得,似乎、彷彿、好像、應該有個for... else語法,可以讓程式漂亮一點。
Thumbnail
其中一顆子彈,就是追不上另外一顆子彈的速度。 我試了好幾次好幾次,滿地都是彈殼, 砰砰兩響幾乎浮貼般地融為一體。但僅止於幾乎而已。 甩甩雙手上的槍,看起來冷冷的的黝黑外殼還是很燙。 為什麼要這樣做? 在那年,我經歷了這件事情,在一次慣例的交易時, 我一如往常地想要便宜行事,這並不是說我這個人很隨便,
Thumbnail
其中一顆子彈,就是追不上另外一顆子彈的速度。 我試了好幾次好幾次,滿地都是彈殼, 砰砰兩響幾乎浮貼般地融為一體。但僅止於幾乎而已。 甩甩雙手上的槍,看起來冷冷的的黝黑外殼還是很燙。 為什麼要這樣做? 在那年,我經歷了這件事情,在一次慣例的交易時, 我一如往常地想要便宜行事,這並不是說我這個人很隨便,
Thumbnail
老鼠誤觸桿子而得到了食物,但得花多久的時間才會「剛好」碰到? 斯金納箱(Skinner box)是行為主義心理學的經典實驗,Skinner設計了一個箱子,把一隻飢餓的老鼠放進箱子之中,老鼠首先會在箱子內探索,直到終於誤觸到箱子內的桿子,並且有食物掉下來,老鼠就得到了食物,並且學會了壓下桿子就能得到食
Thumbnail
老鼠誤觸桿子而得到了食物,但得花多久的時間才會「剛好」碰到? 斯金納箱(Skinner box)是行為主義心理學的經典實驗,Skinner設計了一個箱子,把一隻飢餓的老鼠放進箱子之中,老鼠首先會在箱子內探索,直到終於誤觸到箱子內的桿子,並且有食物掉下來,老鼠就得到了食物,並且學會了壓下桿子就能得到食
Thumbnail
現在很多長期居住在都市的孩子,有的連「魚、水果」的真實樣態都沒有概念,因為馬拉松式的課業壓得他們喘不過氣、因為明亮的現代化超市取代了傳統市場。
Thumbnail
現在很多長期居住在都市的孩子,有的連「魚、水果」的真實樣態都沒有概念,因為馬拉松式的課業壓得他們喘不過氣、因為明亮的現代化超市取代了傳統市場。
Thumbnail
工作坊上,最常聽到的提問是:「老師,我的孩子沉迷在網路遊戲中,怎麼辦?」每次聽到這樣的問題,理智上,我可以理解這是個多麼令人苦惱的難題,但感受上,其實沒多大波瀾。 因為,從小到大,我不曾沉迷於任何一項網路遊戲中,直到這次...
Thumbnail
工作坊上,最常聽到的提問是:「老師,我的孩子沉迷在網路遊戲中,怎麼辦?」每次聽到這樣的問題,理智上,我可以理解這是個多麼令人苦惱的難題,但感受上,其實沒多大波瀾。 因為,從小到大,我不曾沉迷於任何一項網路遊戲中,直到這次...
Thumbnail
註:我帶的書寫課,所有的活動我也參予,下面是經過文字遊戲之後,產生的遊戲句、遊戲文。 經過文字遊戲,可以寫出平常不會寫的句子和文章。 ◆遊戲句 ․剪刀想要剪油漆,剪了又剪,剪不斷,油漆還是油漆。 ․吹桌子啊!吹桌子,再怎麼吹,桌子依然紋風不動。 ․梳子看鐵錘不順眼,要將它梳直。 ․鐵釘黑嚕嚕的不好看
Thumbnail
註:我帶的書寫課,所有的活動我也參予,下面是經過文字遊戲之後,產生的遊戲句、遊戲文。 經過文字遊戲,可以寫出平常不會寫的句子和文章。 ◆遊戲句 ․剪刀想要剪油漆,剪了又剪,剪不斷,油漆還是油漆。 ․吹桌子啊!吹桌子,再怎麼吹,桌子依然紋風不動。 ․梳子看鐵錘不順眼,要將它梳直。 ․鐵釘黑嚕嚕的不好看
Thumbnail
「我要和蛋頭玩一樣的遊戲。」   它永遠記得這句不經意聽到的鄰桌對話,在那個當下,突然建構起它對童年的定義。   在午夜的賭博場外,普通的電動玩具機台都關了,有一群被放飛的孩子在裡面玩鬼抓人, 他們的爸媽跟它的爸爸還在18禁燈火通明的區域尋找人生樂趣,那些孩子被隔離在外,但或許更深知快樂的意義。 他
Thumbnail
「我要和蛋頭玩一樣的遊戲。」   它永遠記得這句不經意聽到的鄰桌對話,在那個當下,突然建構起它對童年的定義。   在午夜的賭博場外,普通的電動玩具機台都關了,有一群被放飛的孩子在裡面玩鬼抓人, 他們的爸媽跟它的爸爸還在18禁燈火通明的區域尋找人生樂趣,那些孩子被隔離在外,但或許更深知快樂的意義。 他
Thumbnail
很多人推薦使用軌跡球滑鼠,因為據說可以改善滑鼠使用量過大,手肘肌肉發炎的徵狀。我也是曾經嘗試過,不過無法習慣,最終還是改回一般滑鼠使用。 她是位老客戶,從剛上研究所時我就認識她,還記得當年她拿了塊478腳位的主機板,請我幫她查修,後來透過更換電容已經恢復運作,直到她研究所畢業。 研究所畢業之後,她帶
Thumbnail
很多人推薦使用軌跡球滑鼠,因為據說可以改善滑鼠使用量過大,手肘肌肉發炎的徵狀。我也是曾經嘗試過,不過無法習慣,最終還是改回一般滑鼠使用。 她是位老客戶,從剛上研究所時我就認識她,還記得當年她拿了塊478腳位的主機板,請我幫她查修,後來透過更換電容已經恢復運作,直到她研究所畢業。 研究所畢業之後,她帶
Thumbnail
家長問:「孩子玩桌遊唔守規則點算好?」 六招讓你教得好又不傷感情!
Thumbnail
家長問:「孩子玩桌遊唔守規則點算好?」 六招讓你教得好又不傷感情!
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News