《零基礎入門學習Python》第083講:Pygame:提高遊戲的顏值3
我們說過,圖像是特定像素的組合,而Surface 對像是Pygame裡面對圖像的描述,在Pygame裡面到處都是Surface 對象,set_mode() 返回的是一個Surface對象,在界面上打印文字也需要先把文字渲染成Surface 對象,然後再貼上去,小蛇在上面爬呀爬,其實就是不斷調整Surface對像上的一些特定的像素的位置,把小蛇所在位置的像素進行移動,就是小蛇在上面爬,就是調用blit() 方法。imag.load() 方法載入圖像並會返回一個Surface 對象,我們此前都是直接拿來用,並沒有進行任何轉換,這樣子就是效率比較低的做法。如果你希望你的Pygame可以盡可能高效的處理你的圖片,你應該在圖片載入之後立刻調用convert() 方法進行轉換。
如:bg = pygame.image.load("bg.jpg").convert()
我們的遊戲事實上都是由各種不同的圖片組成的,例如說:背景是一個圖片、裡面的主角是圖片、反派也是圖片、還有路人甲乙丙都是圖片。你總不可能用一個圓形或者矩陣畫一個主角吧。我們只能在現實中先用Photoshop 畫一個惟妙惟肖的主角,然後貼進去,然後使用blit() 方法讓它移動。
有的人就很好奇了,不是說image.load() 返回一個Surface 對象嗎,那還轉換個毛線啊,這裡的轉換只是像素格式的轉換,而不是說轉換為Surface 對象,因為image.load( ) 載入之後就是一個Surface 對象,但是我們載入之後(例如我們載入一個JPG格式的圖片,JPG圖片也是由像素組成的,而這些像素都是有顏色的,另外我們還可以將這個JPG圖片保存為PNG,GIF格式,你會發現尺寸會發生改變了,這是因為裡面的像素格式發生改變了,也就是說它組合這些像素,把它描述的形式發生改變了),Surface 也有它自己的像素格式,所以我們這裡的轉換指的就是圖片的像素格式的轉換。
如果我們沒有在image.load() 之後立刻對它進行轉換,但是轉換非常重要,Pygame也會你在調用blit() 方法時自動進行轉化,就是將一個圖片貼到另一個圖片之上的時候,因為兩個圖片要進行複制拷貝的操作,它們的像素格式必須相同,因此在每次blit() 的時候,它都會強制轉換一次,這樣子效率就相當低了,與其讓它每次循環去轉換一次,我們還不如在載入時調用convert() 方法轉換為Surface 的像素格式。
雖然現在的CPU速度都很快,這一點細微的差別你可能看不出來,但是我們都希望我們的程序小一點、效率高一點。今後我們都會在image.load() 之後立刻調用convert()。
還有一個就是convert_alpha() 方法:
如:turtle = pygame.image.load("python.png").convert_alpha()
這兩個方法有什麼區別呢?一般情況下,我們使用RGB來描述一個顏色,然而在遊戲開發中,我們常常用到的是RGBA (RGBA是代表Red(紅色) Green(綠色) Blue(藍色)和Alpha的色彩空間)來描述。Alpha通道是用來表示透明度的,A佔用一個字節,也就是8位(0-255,256種層次),用序號來索引,就是0-255,0表示完全透明,255表示完全不透明。
我們都知道,image.load() 支持多種格式的圖片導入,例如gif、jpg、png等,這些都是當前流行的圖片格式,對於包含Alpha 通道的圖片,我們就要用convert_alpha() 方法來轉換格式了,其它的就用conmvert() 方法。
我們也知道,jpg 格式的圖片是不包含Alpha 通道的,因為它不能來表示透明。我們在做圖片的時候,我們知道,兩種常用的透明格式就是png和gif 格式,而gif 還支持動圖,動圖在Pygame 裡面是不能解析的,一般我們在Pygame 裡面做的圖片都是以png 圖片為主,因為jpg 是有損的,你放大縮小它會損失精度,png 是無損壓縮。
jpg 是不支持透明的,所以我們載入這類圖片就用convert();而png 是支持透明的,所以載入就用convert_alpha()。
大家可以看一下下面兩張圖片:

左邊是png 格式的原圖,是透明背景的,當我另存為jpg 格式時,背景就不透明了。
如果你載入左邊的透明png 圖片,使用convert_alpha() 方法轉換和使用convert() 方法轉換,比較一下。
接下來談談Pygame 對透明度的處理。
Pygame 支持三種透明度類型:colorkeys,surface alphas 和pixel alphas(溫馨提示:colorkeys 是指設置圖像中的某個顏色值為透明(比如說上面的烏龜有很多種綠色,我把其中一種綠色變為透明,那麼在這個圖片裡面,與這個顏色相同的部分都不見了,取而代之的是背景的顏色,因為透明事實上就是把背景顯示出來。),surface alphas 是調整整個圖像的透明度,pixel alphas 則是獨立設置圖像中每一個像素的透明度)。
png 就是一個piexl alphas,所以它每個像素都有一個Alpha 通道,指定這個像素是否要變透明,透明度是多少。
surface alphas 可以和colorkeys 混合使用,而pixel alphas 不能和其他兩個混合。
說起來很複雜,其實說白了,convert() 方法轉換出來的就支持 surface alphas 可以和colorkeys 設置透明度,而且他們是可以混合設置的。而convert_alpha() 方法轉換之後呢,就只支持piexl alphas ,也就是說這個圖像本身每個像素就帶有Alpha 通道,我們載入一個帶有Alpha 通道的圖片,我們會看到有一部分是透明的,就像我們上面的小烏龜,它的背景就是透明的。
我們來做一下實驗:
我們這裡有兩張圖片,一種是jpg,背景白色;一張是png,背景透明。
我們首先載入這張jpg 圖片,為了更好區分,我們加了一個背景。

現在我試圖使用set_colorkey() 把所有的白絲變為透明,來看一下效果:

大家可以看到,結果並不優秀,因為邊緣並不是純白色的,結果並不是我們想要的,並不理想。
我們接著使用第二個方法,用set_alpha() 方法來調節整個圖像的透明度,我們把透明度調節為200,來看一下效果:

我們看到整個圖片都變得微微透明了,但是這個把背景也帶上了,我們就想要小烏龜變透明,不想要白色邊框。
我們把兩種方法混合使用:

效果依然不優秀。
我們再來試試png,

由於我們這個png 是帶Alpha 通道的,而且我們在做這個圖片的時候,已經把它的背景給扣成透明的了,下面的陰影不透明,但是不影響美觀(是故意做出的效果)。
我們想把整個小烏龜調為透明度200,讓小烏龜有一種隱身的既視感。
但是我們說這個是piexl alphas,也就是每個像素都有一個Alpha 通道,因此我們不能使用turtle.set_alpha(200) 方法,我們可以使用get_at() 方法來獲得單個像素的透明度,並且用set_at()方法來修改它。
我們先來嘗試是否能夠獲得 單個像素的透明度。
顯然是四元組(R,G,B,A),前面3個值表示RGB顏色,最後的255表示透明度為完全不透明。
這樣子,我們就可以進行修改。
我們寫一個循環,逐個獲取每個像素的顏色,只要不是透明背景的話,就把透明度設為200。

臥槽,效果還是不優秀啊。而且就算效果優秀了,你這樣一個一個像素的來計算透明度,效率未免也太低了吧,那怎麼辦?
沒問題,程序是死的,人是活的,我這裡教大家一個新技能來搞定這個問題:
先看一下解決方案,我們再來分析:

這是如何實現的呢?我們來逐句分析一下:
我們封裝了一個名為blit_alpha() 的函數,下面我們就不要調用blit() 方法了,而是調用 blit_alpha(),因為我們在函數的末尾調用了blit() 方法。
在函數中,我們首先創建一個Surface ,傳進來的是一個帶Alpha 的,我們將其變為不帶Alpha 的,其實我們只是需要它的一個矩形區域,
即:temp = pygame.Surface((source.get_width(), source.get_height())).convert()
temp 就是下圖種黑色矩形的位置,其實就是小烏龜圖片的覆蓋區域範圍。
然後我們在上邊繪製背景
即:temp.blit(target, (-x, -y ))
為什麼是(-x, -y)呢?
我們知道, x = location[0],y = location[1],而形參location對應的實參是position,就是小烏龜圖像的位置,其實(x, y)就是下圖的A點,這個位置是相對於頂點B(0,0)(對於screen 來說,B就是(0,0))來說的,而temp.blit()是相對於小烏龜圖像的來粘貼背景,所以B相對於A點來說,就是(-x,-y)。

然後我們就在A的位置貼上背景透明的小烏龜圖片
即:temp.blit(source, (0, 0))
然後我們將整個temp 區域設為透明度200,其實現在的整個temp 區域(Surface 對象)就是A區域大小,然後背景是該區域的草地,主角是小烏龜的一幅圖片。如圖所示:(這個就是目前的temp了)

我們將這個圖片透明度設為200。
即:temp.set_alpha(opacity) #opacity=200
這就巧妙的避開了帶Alpha通道的Surface 不能調用set_alpha()方法的問題。因為現在的temp 是不帶Alpha通道的。
然後我們就把這個透明度為200的圖片貼在背景屏幕上,
即:target.blit(temp, location)
0 留言:
發佈留言