【引用】pygame菜鳥入門指南

pygame菜鳥入門指南

本文引用自360doc,由於返回去找找原地址已經不可考,就聲明一下引用。下文的不少點在我剛開始使用pygame兩天就解決了不少的疑惑,留在這裏是爲了讓往後的學習有一個參考。雖然不知道做者,仍是感謝一下python

pygame是一個由Pete Shinners編寫的SDL的Python封裝。這就意味着,使用pygame,你能夠寫出能夠不做修改就能夠在任何支持SDL的平臺(Windows, Unix, Mac, BeOS,等等)上運行的遊戲或者多媒體程序。
Pygame不難學,可是圖形編程的世界卻會讓新手糊塗。我撰寫此文的目的,在於抽取出過去幾年來在pygame和它的先驅,pySDL上的工做經驗中得到的實用知識。我已經嘗試着要把這些建議按照它們的重要程度來分等級,可是它(指「重要程度」)卻又與你的背景知識和你的工做細節相關。編程

1.熟練使用Python。

最重要的事情是要有信心使用Python。若是你不熟悉你所使用的語言,那在學習實際上很複雜的東西好比說圖形編程,這將會成爲一個累贅。用python寫幾個有必定規模的非圖形程序——解析某些文本文件,寫一個猜數遊戲或者一個流水分錄程序,或者別的什麼。習慣於字符串和列表操做——學會怎麼樣分割,分片,以及合併字符串或者列表。知道import是怎麼樣工做的——試着一個由數個源文件組成的程序。編寫你本身的函數,而且練習操做數字和字符;知道怎麼樣在它倆之間轉換。直到使用列表和字典已經成爲第二本能——你不用每次分片一個列表或者排序一個關鍵值集合的時候都求助於文檔。當你遇到挫折時,抵制向郵件列表,comp.lang.python,或者irc求救的誘惑。打開解釋器,坐下來跟問題幹上幾個小時。把《Python 2.0 快速指南》打印出來,而且放到電腦邊上。編輯器

聽起來難以置信的無聊,可是在你熟悉python的過程當中所得到的益處卻會在編寫你本身的遊戲時體現出來。跟你之後編寫實戰代碼時省下的時候相比,熟悉python所花的時候不值一提。ide

2.識別pygame的哪些部分是你所須要的。

看着pygame文檔頂部那些雜亂的類要讓人眼花。重要的是認識到只利用這些功能的一個小子集就能夠幫助你完成許多事。許多類你壓根就不會用到——在一年裏,我沒有接觸到過 Channel,Joystick,cursors,Userrect,surfarray或者版本函數。函數

3.知道什麼是surface

Pygame的最重要部分是surface。就把surface 想當成一張白紙吧。你要用對一個surface作許多的事——你能夠在它上面畫線,給它的部分填充顏色,把圖像拷進去或者拷出來,設置或者讀取它上面的某個單獨的像素的顏色值。一個surface能夠是任何大小(能夠理解)而且你要多少就有多少(也能夠理解)。有一個surface是特別的——你用 pygame.display.set_mode()建立的那一個。這個display surface表明了屏幕;你對它作的任何事情都會呈如今用戶屏幕上。你只能有一個這玩意——這是SDL的一個限制,而不是pygame的。性能

可是你怎麼建立surface呢?正如上所說,能夠用 pygame.display_set_mode()來建立特殊的display surface。你能夠用image.load()來建立一個包含了圖像的surface,或者你能夠用font.render()來建立一個包含了文字的surface。甚至你能夠用一個Surface()來建立一個什麼都沒有的surface。學習

. 大部分的surface函數都不是相當緊要的。只要學習blit(),fill(),set_at()和get_at()就夠用了。優化

4.使用surface.convert()

當我第一次閱讀surface.convert()的文檔時,我並無意識到這是我要注意的。「我只使用png格式,既然我用的都是同一個格式,因此我不須要convert()」。它證實了我是很是,很是錯的。動畫

Convert()所指的「格式」並不是指文件格式工(如 png,jpeg,gif),它是所謂的「像素格式」。它表明了一個surface記錄一個特定像素的顏色的方法。若是surface格式跟顯示格式不同,那SDL就要在每次blit的時候去轉化它——這是個至關費時的過程。不用關心解釋,只要注意到若是想在blit以外得到速度,那你就須要 convert()。編碼

那怎麼轉換呢?只需在用image.load()函數建立了一個surface後調用它。不使用: surface = pygame.image.load('foo.png')

Do:用:

surface = pygame.image.load('foo.png').convert()

至關簡單,你只須要當從硬盤中加載圖像的時候,對每一個surface調用它一次。你會對結果滿意的;我發現使用convert()時,blit的速度有了6倍的提升。

你不須要使用convert()的惟一狀況,就是當你真的想對圖像的內部格式有絕對的控制權的時候——好比說你正在寫一個圖像轉換程序或者其它的,可是你要保證輸出文件和輸入文件有相同的像素格式。若是你在寫一個遊戲,你須要速度,就用convert()。

5.髒矩形動畫

Pygame程序中致使幀率不足的的最多見緣由就是誤用了 pygame.display.update()函數。在pygame中,僅僅把東西畫到display surface中並不會讓它顯示在屏幕上——你要調用pygame.display.update()。有三種方法去調用它:

pygame.display.update()——更新整個窗口(或者在全屏顯示下是整個屏幕)。

Pygame.display.flip()——這個幹了相同的活,只是若是你同時使用了雙緩衝硬件加速時它也會幫你該作的事,但若是你沒有,那當什麼都沒發生過……

Pygame.display.update(一個矩形或者矩形列表) ——這個只更新屏幕上你指定矩形區域。

不少圖形編程的新丁使用第一個選擇——他們在每一幀裏更新整個屏幕。問題是這樣作對大多數人來講慢得不能忍受。在個人電腦上調用update()花費35毫秒,聽起來很少,除非你看到一秒最多有1000/35=28 幀,而且仍是沒有任何遊戲邏輯,沒有blit,沒有輸入,沒有人工智能,什麼都沒有。我只是坐在凳子上去更新屏幕,而28fps就是個人最高幀率。啊!

解決方法叫作「髒矩形動畫」。替換每幀更新整個屏幕,而只更新自上一幀已經改變過的部分。我是經過用一個列表來跟蹤這些矩形,而後在幀結束時調用update(the_dirty_rectangles)來實現的。好比說對於一個移動中的精靈,我:

在背景上blit精靈所在的位置,擦掉它。

把精靈的當前區域矩形加到一個叫dirty_rects的列表中去。

移動這個精靈。

在新的區域畫這個精靈。

把精靈的新區域加到個人dirty_rects中去。

調用display.update(dirty_rects)

想一想Solarwolf有大量持續移動的精靈亦平滑更新,而且還有時間去顯示一個視差粒子效果,而且也被更新。

有兩種狀況是用不上這種技術的。其一是當整個窗口或者屏幕的確須要在每一幀被更新——考慮一下一個平滑滾動的引擎,像一個實時戰略遊戲或者一個邊滾動條。那在這種狀況下你應該用什麼?呃,簡單的答案就是不要在 pygame中寫這種遊戲。而詳細的答案是一個步驟裏滾動數個像素,試要企圖作出絕對平滑的滾動。玩你遊戲的人會喜歡一個滾動得快的遊戲,而不太會注意到背景的跳躍。

最後一點——不是全部的遊戲都須要高幀率。一個策略戰爭遊戲在一秒鐘內只須要更新幾回就足夠了——在這種狀況下,髒矩形動畫帶來的複雜性是多餘的。

6 . 硬件surface弊大於利。

若是你已經看過能夠用在pygame.display.set_mode()中可使用的衆多標誌值時,你可能會這樣想:「嘿,HWSURFACE!嗯,我須要它——誰不喜歡硬件加速?噢……DOUBLEBUF;嗯,聽起來挺快的,我看我也須要它!」這不是你的錯,咱們經受過多年的3D遊戲訓練,已經默認了把硬件加速是優秀的,而軟件加速是緩慢的。很不幸,硬件渲染天生就有一長串的缺陷:

它只在能在某些平臺上運行。在Windows上,你一般能夠獲得硬件surface。大多數的其它平臺卻不能。好比說Linux,它也許會提供硬件 surface,若是安裝了X4,若是DGA2正常運行起來,若是moons也被正確對齊了。若是不能給你硬件surface,SDL會悄悄地給你一個軟件surface。
它只能在全屏模式下工做.

它複雜化了每一個像素的訪問。若是你有一個硬件surface,你必須在讀寫單獨的像素值的時候鎖定屏幕。若是你不這樣作,就會壞了大事。接着你又要趕在系統被搞蒙而且開始抱怨以前立刻解鎖。這些過程都是pygame自動爲你作的,可是它值得注意。

你沒有了屏幕光標。若是你指定要HWSURFACE(而且真的拿到手了),你的光標一般會消失掉(更嚴重的是隻剩下半個在一閃一閃的)。你只有建立一個精靈來扮演鼠標光標的角色,而且還要承擔光標加速和敏感度的責任。真煩人。

它有可能變得更慢。許多驅動程序並不會加速咱們做圖類型,而且全部東西都必須經過視頻總線來blit(除非你能用源surface來充滿顯存),結果就是比軟件訪問更慢。

硬件渲染有它存在的理由。在Windows下它運行地至關可靠。因此若是你不關於跨平臺的性能時,它能夠給你帶來看得見的速度提高。不過,它也有代價——更多的頭疼和複雜性。除非你知道你在幹什麼,不然最好就是堅持使用較好可信賴的的SWSURFACE。

7.不要糾纏於細枝末節。

有時候,遊戲編程新人在某些對遊戲的成功並不是緊要的地方花了太多時間。把將要的要素作「對」是可理解的,可是在創做遊戲的早期,你並不知道哪些是重要的問題,更不要說你應該選擇的答案了。結果就是帶來一堆的藉口。

舉例說,思考一下怎麼樣組織你的圖形文件的問題。是每一幀有它本身的圖像文件好呢,仍是每一個精靈都有?或者把全部的圖像都打包成一個壓縮包?許多項目的許多時間被浪費在在郵件列表提問,爭論問題的答案,比較,等等等等。這些都是次要矛盾;花在爭論上的時間本來都應該用到編碼實戰遊戲中去。

這裏的主旨就是說,一個已經實現了的「恰當地好」的解決方案,要遠遠優於一個沒有開始動手的完美的解決方案。

8.Rect是你的好朋友

Pete Shinners的封閉可能有很酷的alpha效果,和快速的blit速度,可是我不得不說我最喜歡pygame部分是底層的Rect類。一個rect就是一個矩形——由它左上角的位置,它的寬度,它的高度定義。Pygame的許多函數都用rect做參數,也接受「矩形形式」,一個跟rect有相同值的序列。所以,若是我須要一個位於10, 20和40, 50之間的區域時,我能夠作如下幾個中的一個:

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))
若是你使用開頭三個中的任何一個,你就可使用rect的實用函數。它們包括移動,收縮和膨脹矩形,找出兩個矩形的並集,和一堆的碰撞檢測函數。

例如,假設我想獲得包含了點(x, y)——可能用戶點擊了這裏,也多是子彈的當前位置——的全部精靈。若是每一個精靈都有一個.rect成員,事情就會很簡單——我只消:

sprites_clicked = [sprite for sprite in all_my_sprites_list if sprite.rect.collidepoint(x, y)]

除了能用rect做爲參數,rect 跟surface和圖像函數沒別的瓜葛。你也能夠把它們用到跟圖形沒啥關係,卻又須要矩形的地方去。我幾乎在每一個工程裏都發現幾個須要使用rect的地方,而我歷來沒想到我也要在這裏用上它們。

9.不要對像素級的碰撞檢測費心

至此你已經讓你的精靈動了起來,你須要知道他們到底會不會撞上別人。像下面這樣作是頗有誘惑性的:

看看矩形是否是碰撞了,若是不是,忽略它們。

對於重疊區域的每個像素,檢查它在每一個精靈對應的像素是否是不透明的,若是的確是這樣,則它們碰撞了。

有不少方法實現這個想法,對精靈進行「與運算」等,但不管如何,它都會很慢。甚至對多數遊戲來,更恰當的是作「子矩形碰撞」——對每一個精靈作一個比其真實圖像略小的矩形,用它來進行碰撞。這樣會快得多,而且大數多狀況下玩家不會注意到這個不精確的作法。

10.管理好事件子系統

Pygame的事件系統很巧妙。有兩種不一樣的方法找出輸入設備(鍵盤,鼠標或遊戲控制桿)在作什麼。

第一種方法是直接檢查設備的狀態。經過調用叫pygame.mouse.get_pos()或pygame.key.get_press()的方法來實現。它們會告訴你當你調用這函數時設備的狀態。

第二種方法是使用SDL的事件隊列。這個隊列是一個事件的列表——當事件發生時就會被加到列表中,若是被讀過了,它們就會被刪除掉。

這兩套系統各有利弊。狀態檢查(第一套系統)很精確——你準確知道一個輸入何時發生——若是mouse.get_pressed([0])的值是1,說明那時的鼠標左鍵點下了。而事件隊列僅僅告訴咱們在過去某個時間鼠標被點下了;若是你很頻繁地檢查隊列,那沒事,但若是你延遲了檢查,潛在的輸入就會愈來愈多。狀態檢查系統的另外一個優點就是能夠很方便地檢測「和音 」,也就是說,在同一時間裏同時生了多種狀態。若是你要檢查t和f鍵是否是被同時按下了,只要檢查:

if (key.get_pressed[K_t] and key.get_pressed[K_f]):
print "Yup!"

在隊列系統中,每一個到達隊列的按鍵都做爲一個單獨的事件,因此你必須記得,在你檢查f鍵的時候,t鍵被按下了但沒有彈起。有點複雜。

但狀態系統有一個巨大的缺陷。它只報告被調用的那一刻的設備狀態。若是鼠標被按下了,而且在釋放以前調用了mouse.get_pressed(),鼠標會返回0——get_pressed()徹底錯過了鼠標按下事件。這兩個事件,MOUSEBUTTONDOWN和MOUSEBUTTONUP,仍然被放到事件隊列中去,等待讀取和處理。

教訓就是:選擇最適合你的須要的系統。若是不須要在循環中作太多事——也就是說你只需坐在板凳上,在一個'while 1'循環中待輸入,用get_pressed()或者其它狀態函數,延遲會比較短。相反,若是每個按鍵都很關鍵,可是延遲不那麼重要——好比你的用戶在一個編輯框裏輸入某些東西,就使用事件隊列。某些按鍵會有點延遲,但至少你會一個不缺。

關於event.poll()和wait()對比的註記—— poll()看起來會好點,由於它在等待輸入的時候不會阻塞你的程序作事。Wait()則掛起程序,直到接收到一個事件。不過,poll()運行時會花掉全部可用的CPU時間,而且它會用NOEVENTS來塞滿事件隊列。用set_blocked()來選擇你只須要哪些事件類型——你的隊列看起來會更好管理。

11.色鍵 vs Alpha

這兩種技術經常混淆,而且大多來自於對術語的誤用。

「色鍵」告訴pygame,特定圖像的具備某種特定顏色的全部像素都被當成透明的。當其它部分的圖像被blit的時候,這些透明像素不會被blit,因此不會破壞背景。這就是咱們怎麼樣使得精靈的形狀不是矩形的。簡單調用surface.set_colorkey(color),其中color是一個RGB元組——像(0, 0, 0)。它會把圖像中黑色的像素看成透明的。

「Alpha」則不一樣,它有兩種形式。「圖像Alpha」應用於整個圖像,這一般是你須要的。如你們所知的「半透明」,alpha讓源圖像的每一個像素只有部分不透明。好比說,你設置了一個surface的alpha爲 192,而後把它blit到一個背景上去時,每一個像素的3/4顏色值會來自源圖像,而1/4來自背景圖像。Alpha用255到0的一個數來衡量,其中0 表示徹底透明,255表示徹底不透明。注意,顏色鍵和alpha能夠混合——產品就是一部分徹底透明另外一部分半透明的圖像。

「按像素alpha」是alpha的另外一種表現,它更加複雜。簡單說,源圖像上每一個像素都有它本身的alpha值,從0到255。在blit到背景上的時候,每一個像素都有一個不一樣的不透明度。這種類型的alpha不能跟色鍵混使用,而且要覆蓋整個圖像alpha。遊戲中不多會用到按像素alpha的,若是要用它,你要先用一個圖像編輯器讓它保留下一個alpha通道。很複雜——不要用它了。

12.用Python的方法去幹活。

最後一個要點(這並非最不重要的一個,只是它剛好應該放在最一個說)了。Pygame是SDL的一個優秀的輕量級封裝,同時也是你本地操做系統圖形調用的一個完美的輕量級封裝。若是你的代碼依然很慢,而你已經作完了我上面提到的事情,那麼大好機會來了,你所面對的問題就是你怎麼樣組合Python代碼形成的。某些運行得很慢的慣用語跟你所作的事情沒什麼關係。很幸運,python是一個很純淨的語言——若是一段代碼看起來很笨拙不實用,那麼就有機會改善它的運行速度。閱讀關於提升Python性能的技巧,從中獲取提升你的代碼速度的建議。有人說,過早的優化是萬惡之源。若是它只是不夠快,不要改動代碼來嘗試讓它更快。某些東西並不必定要那樣。

就這樣吧。如今你知道了全部我所知道的關於的pygame的東西。如今,寫你的遊戲去吧

相關文章
相關標籤/搜索