Pygame是SDL的一個(gè)python封裝,由PeteShinners編寫。使用pygame,你可以用Python寫游戲或其它的多媒體應(yīng)用程序,它們將穩(wěn)定地運(yùn)行在SDL支持的任何平臺(tái)上(Windows,Unix,Mac,beOS和其它等等)。
Pygame容易學(xué)習(xí),但是圖形編程的世界對(duì)于新來者來說,可能是相當(dāng)混亂的。我寫這篇文章的目的是試圖提煉出我過去那些年來所獲得的或使用pygame的得到的那些有用的知識(shí)。Pygame的前身是pySDL。我是按照這些問題的重要性的次序來排列的,但是這些心得對(duì)你的幫助程序取決于你自己的知識(shí)背景和你的項(xiàng)目的情況。
1、熟練使用python
如果你使用你不熟悉的語言進(jìn)行圖形編程的話,學(xué)起來會(huì)比較復(fù)雜的。用Python寫一些量不大的非圖形化程序——分析一些文本文件,寫一些競猜類游戲或記帳類程序或其它等等。輕松地處理字符串和列表——知道如何去分離(split),切片(slice)和合并字符串和列表。知道如何導(dǎo)入(import)——試著寫一個(gè)程序,該程序的代碼分散在幾個(gè)源文件中。寫你自己的函數(shù),并練習(xí)處理數(shù)字和字符;知道如何在二者之間作轉(zhuǎn)換。要記住使用列表和字典的語法——你不會(huì)想每次需要對(duì)一個(gè)列表進(jìn)行切片或?qū)σ惶钻P(guān)鍵字分類時(shí)再去查看相應(yīng)的文檔。當(dāng)你遇到麻煩時(shí),盡量不要去訪問郵件列表或聊天室。相反,應(yīng)該運(yùn)行python的解釋器并對(duì)問題進(jìn)行幾個(gè)小時(shí)的調(diào)試。經(jīng)常翻閱相應(yīng)Python版的手冊(cè)。
當(dāng)然這些聽起來可能相當(dāng)?shù)目菰?,但是通過你對(duì)python的熟悉,當(dāng)你再用它寫你的游戲時(shí),你會(huì)感到非常的有把握。并可大大提高工作效率。
2、明白自己真正需要pygame的哪些部分。
面對(duì)著pygame文檔索引頂部的那些混雜的類,可能讓人感到糊涂。但是你應(yīng)該認(rèn)識(shí)到,我們只需要這些功能的一小部分就可以完成大量的處理任務(wù)了。有許多的類,你可能幾乎都不會(huì)用到。
3、知道surface是什么
pygame的最重要的部分就是surface。只需要把一個(gè)surface看作一頁白紙就行了。你可以使用一個(gè)surface做很多的事情——你可以在其上繪制線條,在它上面的部分區(qū)域填充顏色,拷貝圖像到它上面和拷貝它上面圖像,設(shè)置或讀取它上面單個(gè)像素的顏色。一個(gè)surface可以是任意大小的尺寸(合理的),并且你想要多少surface,就可以創(chuàng)建多少個(gè)surface(同樣,要合理)。有一個(gè)surface是比較特殊的——你只能使用pygame.display.set_mode()創(chuàng)建僅一個(gè)。這個(gè)“displaysurface”代表屏幕;你對(duì)它所做的一切都將顯示在用戶的屏幕上。
那么如何創(chuàng)建surface呢?如上所述,你可以使用pygame.display.set_mode()來創(chuàng)建這個(gè)特殊的“displaysurface”。你可以通過使用image.load()來創(chuàng)建一個(gè)包含一個(gè)圖像的surface,你可以使用font.render()創(chuàng)建包含文本的一個(gè)surface。你甚至可以使用Surface()創(chuàng)建一個(gè)什么也不包含的surface。
surface的大多數(shù)的函數(shù)都不是關(guān)鍵性的,你只需要學(xué)習(xí)blit(),fill(),set_at(),get_at()就很好了。
4、使用surface.convert()
當(dāng)我第一次閱讀surface.convert()的文檔的時(shí)候,我并沒有在意它。我當(dāng)時(shí)所想的是“我只使用png格式,所以我所做的都將使用相同的格式。因此我不需要convert()”。結(jié)果我是非常錯(cuò)誤的。
convert()指的格式不是文件格式(如png,jpeg,gif),它是指“像素格式”。是指surface記錄一個(gè)特定像素中的單個(gè)顏色的特殊方法。如果surface格式與顯示器格式不同,那么SDL將不得不對(duì)每個(gè)blit在傳輸過程中作出轉(zhuǎn)換——很明顯,這是一個(gè)耗時(shí)的過程。對(duì)于這些解釋你不必考慮太多;如果你想提高你的blit速度,那么只需記住必須使用convert()就可以了。
如何使用convert()?只需要在使用image.load()函數(shù)創(chuàng)建了一個(gè)surface后調(diào)用它就可以了。使用surface=pygame.image.load('foo.png').convert()來替換surface=pygame.image.load('foo.png')。
這很容易。當(dāng)你從磁盤載入一個(gè)圖像時(shí),你只需要對(duì)每個(gè)surface調(diào)用convert()一次。你會(huì)得到滿意的結(jié)果;blit的速度得到大大的提高。
只有在你真正需要絕對(duì)控制一個(gè)圖像的內(nèi)在的格式時(shí),你才不想使用convert()——比如說你正在寫一個(gè)圖像轉(zhuǎn)換程序或什么,并且你需要確保所輸出的文件與輸入的文件有相同的像素格式。如果你是在寫一個(gè)游戲,那么你需要的是速度。請(qǐng)使用convert()。
5、繪制rect(矩形區(qū)域)動(dòng)畫
在pygame程序中,產(chǎn)生不恰當(dāng)?shù)膸l率的原因是對(duì)pygame.display.update()函數(shù)的不理解。使用pygame,僅僅繪制一些東西到“displaysurface”不會(huì)導(dǎo)致立即顯示到屏幕上——你需要調(diào)用pygame.display.update()。調(diào)用該函數(shù)有三種方法:
*pygame.display.update()——這將更新整個(gè)窗口(對(duì)于全屏顯示來說,將更新整個(gè)屏幕)。
*pygame.display.flip()——這個(gè)做相同的事情,并且如果你在使用雙緩沖硬件加速的話,它也會(huì)做正確的處理。
*pygame.display.update(矩形或矩形的列表)——這只更新你指定的屏幕區(qū)域。
多數(shù)圖形編程的新手都使用第一個(gè)方案——它們對(duì)于每幀都更新整個(gè)屏幕。緩慢的速度這一問題就產(chǎn)生了,大多數(shù)人對(duì)于這一速度是不可接受的。在我的機(jī)器上,調(diào)用update()要花35毫秒,聽起來也沒花多少時(shí)間呀,除非你認(rèn)識(shí)到1000/35=28,就是說最快每秒28幀。這不符合游戲的邏輯,難道就不blit,不輸入,不進(jìn)行音頻輸入?我其它什么也不做,就在哪里更新屏幕,這樣才能達(dá)到最大的幀頻28。啊...暈!
解決之道就是所謂的“繪制區(qū)域動(dòng)畫”。代替每幀更新整個(gè)屏幕,我們只對(duì)幀所導(dǎo)致的部分區(qū)域的改變作更新。我通過跟蹤一個(gè)列表中的那些矩形來實(shí)現(xiàn)這個(gè),然后在結(jié)束時(shí)調(diào)用update。對(duì)于移動(dòng)子畫面(sprite)的具體描述如下:
1、blit(傳送)一塊背景到子畫面(sprite)對(duì)象的當(dāng)前位置上,使用這塊背景來抹去(覆蓋)當(dāng)前位置的子畫面。
2、把該子畫面的當(dāng)前位置矩形添加到一個(gè)所謂“待更新矩形”的列表中。
3、移動(dòng)子畫面對(duì)象。
4、將子畫面繪制到它的新的位置
5、將子畫面的新位置添加到“待更新矩形”的列表中。
6、調(diào)用display.update(待更新矩形列表)。
這與以前在速度上的區(qū)別是令人驚訝的。
有兩種情況,你不會(huì)使用這種技術(shù)。首先就是每幀都要更新整個(gè)窗口和屏幕的情況下——考慮面對(duì)像一個(gè)實(shí)時(shí)空中戰(zhàn)略類的游戲或滾動(dòng)條這類的平滑過度,你怎么做呢?簡短的回答是:不用pygame寫這類游戲。長點(diǎn)答案是我們可以一次移動(dòng)幾個(gè)像素的距離;用不著使過度十分地平滑。這不會(huì)影響到玩家的游戲感覺。
最后要說明的是——不是每個(gè)游戲都要求高的幀頻。一個(gè)戰(zhàn)略類的戰(zhàn)爭游戲可能通過每秒僅作少量的更新就可容易地得到——在這種情況下,局部的更新所帶來的復(fù)雜性是沒有必要的。
6、硬件surface的麻煩性多于它們的使用價(jià)值
如果你已經(jīng)注意到了使用pygame.display.set_mode()可用到的各種標(biāo)記,你可能會(huì)有這樣的想法:“嘿,HWSURFACE!嗯,我喜歡——誰不喜歡硬件加速。哦...雙緩沖;嗯,聽起來感覺一定很快,我想我也想要!”這不是你的錯(cuò)誤;我也曾經(jīng)在多年的三維游戲制作中相信硬件加速比較好,軟件表現(xiàn)較慢。
不幸的是,硬件表現(xiàn)帶來了一系列的缺點(diǎn):
*它僅在某些平臺(tái)上工作。Windows的機(jī)器通??梢缘玫接布urfaces,如果你需要的話。其它大多數(shù)的平臺(tái)則不行。例如Linux,如果X4被安裝了,如果DGA2工作正常,如果可能能夠提供硬件surface,如果moons正確地對(duì)齊了,那么有可能能夠提供硬件surface。如果一個(gè)硬件surface是無效的,那么sdl將默默地使用一個(gè)軟件surface來代替。
*它只能工作在全屏狀態(tài)下。
*它使得每個(gè)像素的訪問復(fù)雜化了。如果你有一個(gè)硬件surface,那么在其上寫或讀單個(gè)像素值之前,你需要鎖住這個(gè)surface。如果你不這樣做,將發(fā)生壞的情況。之后,在操作系統(tǒng)整個(gè)混亂之前,你又需要迅速地解鎖。在pygame中這個(gè)過程大部是自動(dòng)的,但還需要考慮剩下的部分。
*你失去了鼠標(biāo)指針。如果你批定HWSURFACE(并且實(shí)際的得到了它),你的鼠標(biāo)指針通常將立即消失(或時(shí)隱時(shí)現(xiàn)的狀態(tài))。你將需要?jiǎng)?chuàng)建一個(gè)子畫面來作為手工的鼠標(biāo)指針,并且你必須考慮指針的加速和靈敏度?;钍茏铩?/p>
*它可能會(huì)更慢。許多的驅(qū)動(dòng)程序是沒有被加速的,并且由于所有的東西必須通過視頻總線傳送(除非你能夠?qū)⒛愕脑磗urface放入視頻儲(chǔ)存器),這可能導(dǎo)致比軟件訪問更慢。
硬件表現(xiàn)有它的地方。在Windows下它工作的十分可靠,所以如果你對(duì)跨平臺(tái)不感興趣,那么它可能給你帶來速度上的巨大的提高。然而,它也更加的令人頭痛和復(fù)雜。最好是堅(jiān)持使用可靠的SWSURFACE,除非你確信知道你正在做什么。
7、不要因枝節(jié)問題而分心。
有時(shí)候,新的游戲程序員在對(duì)他們的游戲的成功方面不是真正關(guān)鍵的問題上花費(fèi)了太多的時(shí)間。想要解決這些問題的心態(tài)是可理解的,但在一個(gè)游戲的創(chuàng)建過程中,還為之尚早,你甚至無法知道重要問題是什么,更何談你應(yīng)該做何選擇。結(jié)果可能是白費(fèi)工夫。
例如,考慮如何組織你的圖形文件的問題。每個(gè)幀或子畫面都應(yīng)該考慮它自己的圖形文件嗎?或許所有的圖形都應(yīng)該被壓縮到一個(gè)文件?大量的時(shí)間被浪費(fèi)在向郵件列表詢問問題,辨論答案上了。這是次要的問題,花在這些問題上的時(shí)間應(yīng)該用在編寫游戲代碼上。
8、Rect是你的朋友
雖然PeteShinners的包(Pygame)有酷的alpha效果和快速的blit速度,但是我必須得承認(rèn),我所喜歡的是它的Rect類(一個(gè)較低級(jí)的部分)。rect是一個(gè)簡單的矩形——僅由它的左上角和它的寬高所定義。許多pygame的函數(shù)都要求rect或矩形的序列作為參數(shù)。因些,如果我需要一個(gè)大小為左上角坐標(biāo)(10,20)和寬40,高50的矩形區(qū)域,我可以如下來得到:
rect=pygame.Rect(10,20,30,30)
rect=pygame.Rect((10,20,30,30))
rect=pygame.Rect((10,20),(30,30))
rect=(10,20,30,30)
rect=((10,20,30,30))
然而如果你使用上面前三個(gè)的任一個(gè),你將可以通過rect訪問rect的實(shí)用的功能。這包括移動(dòng)、縮小或增大rect(矩形)、發(fā)現(xiàn)兩個(gè)rect(矩形)相交、和各種碰撞檢測(cè)功能。
例如,假設(shè)我想得到所有包含點(diǎn)(x,y)的子畫面(sprite)的一個(gè)列表——或許玩家在此點(diǎn)敲擊了,或許該點(diǎn)是一個(gè)子彈的當(dāng)前位置。如果每個(gè)子畫面(sprite)都有一個(gè).rect成員的話,就簡單了,我可以就這么實(shí)現(xiàn):
sprites_clicked=[spriteforspriteinall_my_sprites_listifsprite.rect.collidepoint(x,y)]
實(shí)際上除了你可以使用rect作為surface或圖形函數(shù)的參數(shù)以外,rect與surface或圖形函數(shù)沒有其它的關(guān)系。在某些與圖形無關(guān)的地方,你也可以使用rect,但是它們?nèi)匀恍枰欢x為矩形。對(duì)于rect用處很少的項(xiàng)目,我不會(huì)考慮使用它們。
9、不要考慮對(duì)全部的像素做碰撞檢測(cè)。
如果你已經(jīng)實(shí)現(xiàn)了子畫面的移動(dòng),你需要知道它們是否碰撞到了一起。那么你很有可能像下面這樣做:
1、檢查rect是否相撞。如果沒有就忽略。
2、對(duì)于在重疊區(qū)域中的每個(gè)像素,檢查兩個(gè)子畫面中的對(duì)應(yīng)像素是不是不透明的。如果是,則產(chǎn)生了碰撞。
也有其它的檢測(cè)方法,可以對(duì)子畫面的遮罩作與運(yùn)算等等,但是在pygame中你所使用的這些方法,處理起來可能太慢。對(duì)于大多數(shù)游戲,或許只作一個(gè)子區(qū)域碰撞會(huì)更好——為每個(gè)子畫面(sprite)創(chuàng)建一個(gè)比實(shí)際圖像更小的rect(矩形),以用來作碰撞。這樣檢測(cè)會(huì)更快,雖然不太精確。
10、管理事件子系統(tǒng)
Pygame的事件系統(tǒng)是機(jī)智的。有兩種不同的方法用來發(fā)現(xiàn)哪個(gè)輸入設(shè)備(鍵盤、鼠標(biāo)或操縱桿)正在動(dòng)作。第一種是通過直接檢查設(shè)備的狀態(tài)。你可以通過調(diào)用pygame.mouse.get_pos()或pygame.key.get_pressed()來實(shí)現(xiàn)。這將告訴你相關(guān)設(shè)備在函數(shù)(如pygame.mouse.get_pos()或pygame.key.get_pressed())調(diào)用時(shí)的狀態(tài)。
第二種方法是使用SDL事件隊(duì)列。該隊(duì)列是事件的一個(gè)列表——當(dāng)事件被檢測(cè)到時(shí)事件被添加到該列表,當(dāng)事件被從隊(duì)列中讀取后,該事件將被刪除。
每個(gè)系統(tǒng)都有優(yōu)點(diǎn)和缺點(diǎn)。狀態(tài)檢查(system1)讓你可以準(zhǔn)確地知道給定輸入設(shè)備的狀態(tài)——如果mouse.get_pressed([0])是1,意思就是此刻鼠標(biāo)左按鍵被按下了。事件隊(duì)列只報(bào)告鼠標(biāo)在過去某時(shí)被按下;如果你經(jīng)常均等機(jī)會(huì)地檢查事件隊(duì)列,那么還可以,但是使用額外的代碼檢查會(huì)有延遲。狀態(tài)檢查系統(tǒng)的另一個(gè)好處是容易檢測(cè)“chording”,也就是同時(shí)產(chǎn)生的幾個(gè)狀態(tài)。如果你想知道t和f鍵是否被同時(shí)按下,只需要如下檢查:
if(key.get_pressed[K_t]andkey.get_pressed[K_f]):
print"Yup!"
然而在隊(duì)列系統(tǒng)中,每個(gè)按鍵動(dòng)作在隊(duì)列中是作為一個(gè)完全分離的事件的,因此,在檢查f鍵時(shí),你需要去記住t鍵是否被按下了,并且還沒有恢復(fù)。有點(diǎn)復(fù)雜。
然而,狀態(tài)系統(tǒng)有一個(gè)很大的缺點(diǎn)。它只報(bào)告設(shè)備在函數(shù)調(diào)用時(shí)刻的狀態(tài);如果用戶在調(diào)用mouse.get_pressed()之前敲擊并釋放了鼠標(biāo),那么函數(shù)返回0——get_pressed()完全忽視了鼠標(biāo)的按下。然而對(duì)于隊(duì)列系統(tǒng),這兩個(gè)MOUSEBUTTONDOWN和MOUSEBUTTONUP事件仍將存于事件隊(duì)列中,等待被獲取和處理。
這個(gè)教訓(xùn)就是:選擇適合你的要求的事件系統(tǒng)。如果在你的循環(huán)中,你沒有太多的事情——就是說,你只是待在一個(gè)'while1'循環(huán)中,等待輸入,那么可以使用get_pressed()或另外的狀態(tài)函數(shù)。在另一方面,如果按鍵動(dòng)作是不間斷的,延遲不太重要——比如你的用戶正在一個(gè)編輯框中鍵入一些東西,可以使用事件隊(duì)列,因?yàn)樗鼈冏罱K都會(huì)被處理。
一個(gè)要注意的是,event.poll()與event.wait()的比較——poll()可能更好一些,因?yàn)樗诘却斎霑r(shí)不會(huì)阻塞你的程序去做另外的事情——而wait()會(huì)將程序掛起直到一個(gè)事件被接受。然而poll()在運(yùn)行時(shí)將用掉全部可用的cpu時(shí)間,并且它將使用NOEVENTS來填充事件隊(duì)列。使用set_blocked()來只選擇你感興趣的事件類型——你的隊(duì)列將容易維護(hù)的多。
11、Colorkey與Alpha的比較
圍繞著這兩種技術(shù),有很多不太清楚的地方,大多是因?yàn)樾g(shù)語的關(guān)系。
'Colorkeyblitting'意味著告訴pygame,在某個(gè)圖像中有著某種顏色的所有像素都是透明,取代了它們將是什么顏色。當(dāng)圖像的其余部分被blit時(shí),這些透明的像素不被blit,并且因些不掩蓋背景。這就是我們?nèi)绾问棺赢嬅?sprite)不是矩形的方法。簡單地調(diào)用surface.set_colorkey(color),這里的color是一個(gè)rgb元組——比如(0,0,0)。這將使用源圖像的黑色的像素變成透明的。
'Alpha'不同,它有兩種。'Imagealpha'適用于整個(gè)圖像,并且大概也是你想要的。它用于設(shè)置不透明度。如果你設(shè)置一個(gè)surface的alpha為192,然后把它blit到一個(gè)背景上,那么每個(gè)像素顏色的3/4來自于源圖像,1/4取自背景。Alpha的值的范圍是255~0,0是完全透明,255是完全不透明。注意,colorkey和alpha在blit時(shí)可以被合并——這導(dǎo)致一個(gè)圖像在一些點(diǎn)是完全透明的,而別處是半透明的。
'Per-pixelalpha'是alpha的另一種,它更復(fù)雜。源圖像中的每個(gè)像素都有它自己的alpha值,從0到255。因此,當(dāng)每個(gè)像素被blit到一個(gè)背景上時(shí)可以有不同的不透明度。這種alpha不能與colorkey合并blit,并且它覆蓋單位圖像的alpha。'Per-pixelalpha'很少用在游戲中,要使用它,你必須在你的一個(gè)圖形編輯器中使用一個(gè)特殊的alphachannel來保存你的源圖像。它比較復(fù)雜——根本不要使用它。
12、工作的方式。
Pygame是一個(gè)優(yōu)秀的輕量級(jí)的SDL包。如果你已經(jīng)按我上面提及的方法做了,但是你的代碼的速度仍然慢,那么問題就在于你用python處理數(shù)據(jù)的方法。某些慣用的作法只會(huì)降低你的速度,不管你做什么。所以我們也可以作一些改變,雖然代碼可能看上去很笨拙,但有可能會(huì)提高速度。至于如何提高代碼的速度,你可以看一下相關(guān)的主題。過早的優(yōu)化是所有不好的結(jié)果的根源;如果代碼只是不足夠快,那么就不要再費(fèi)力地想讓它更快了。
以上內(nèi)容為大家介紹了python的新手指南,希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。http://m.fengjieshuijing.cn/