用Pygame和Python做遊戲-從入門到精通(6)

pythontab發表於2012-12-20

用Python和Pygame寫遊戲-從入門到精通(6)

掌握了小小的畫素,我們可以使用更加複雜一點的東西了,對,就是影像,無數的畫素的集合~還記得上次我們為了生成的一張圖片,花了無數時間,還好一般遊戲不會在遊戲的過程中動態生成影像,都是將畫好的作為資源封裝到遊戲中。對2D遊戲,影像可能就是一些背景、角色等,而3D遊戲則往往是大量的貼圖。

雖然是基礎,這裡還是要羅嗦一下,之前說的RBG影像,在遊戲中我們往往使用RGBA影像,這個A是alpha,也就是表示透明度的部分,值也是0~255,0代表完全透明,255是完全不透明,而像100這樣的數字,代表部分透明。你可以使用多種軟體建立含有Alpha通道的圖片,具體的網上查查吧……

這個世界上有很多儲存影像的方式(也就是有很多圖片格式),比如JPEG、PNG等,Pygmae都能很好的支援,具體支援的格式如下:

 

  • JPEG(Join Photograhpic Exper Group),極為常用,一般字尾名為.jpg或者.jpeg。數位相機、網上的圖片基本都是這種格式。這是一種有失真壓縮方式,儘管對圖片質量有些損壞,但對於減小檔案尺寸非常棒。優點很多隻是不支援透明。
  • PNG(Portable Network Graphics)將會大行其道的一種格式,支援透明,無失真壓縮。對於網頁設計,軟體介面設計等等都是非常棒的選擇!
  • GIF 網上使用的很多,支援透明和動畫,只是只能有256種顏色,軟體和遊戲中使用很少
  • BMP Windows上的標準影像格式,無壓縮,質量很高但尺寸很大,一般不使用
  • PCX
  • TGA
  • TIF
  • LBM, PBM
  • XPM

使用Surface物件

對於Pygame而已,載入圖片就是pygame.image.load,給它一個檔名然後就還給你一個surface物件。儘管讀入的影像格式各不相同,surface物件隱藏了這些不同。你可以對一個Surface物件進行塗畫、變形、複製等各種操作。事實上,螢幕也只是一個surface,pygame.display.set_mode就返回了一個螢幕surface物件。

建立Surfaces物件

一種方法就是剛剛說的pygame.image.load,這個surface有著和影像相同的尺寸和顏色;另外一種方法是指定尺寸建立一個空的surface,下面的語句建立一個256×256畫素的surface:

1
bland_surface = pygame.Surface((256, 256))

如果不指定尺寸,那麼就建立一個和螢幕一樣大小的。

你還有兩個引數可選,第一個是flags:

  • HWSURFACE – 類似於前面講的,更快!不過最好不設定,Pygmae可以自己最佳化。
  • SRCALPHA – 有Alpha通道的surface,如果你需要透明,就要這個選項。這個選項的使用需要第二個引數為32~

第二個引數是depth,和pygame.display.set_mode中的一樣,你可以不設定,Pygame會自動設的和display一致。不過如果你使用了SRCALPHA,還是設為32吧:

 

1
bland_alpha_surface = pygame.Surface((256, 256), flags=SRCALPHA, depth=32)

轉換Surfaces

通常你不用在意surface裡的具體內容,不過也許需要把這些surface轉換一下以獲得更高的效能,還記得一開始的程式中的兩句話嗎:

1
2
background = pygame.image.load(background_image_filename).convert()
mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()

第一句是普通的轉換,相同於display;第二句是帶alpha通道的轉換。如果你給convert或者conver_alpha一個surface物件作為引數,那麼這個會被作為目標來轉換。

矩形物件(Rectangle Objects)

一般來說在制定一個區域的時候,矩形是必須的,比如在螢幕的一部分畫東西。在pygame中矩形物件極為常用,它的指定方法可以用一個四元素的元組,或者兩個二元素的元組,前兩個數為左上座標,後兩位為右下座標。

Pygame中有一個Rect類,用來儲存和處理矩形物件(包含在pygame.locals中,所以如果你寫了from pygame.locals import *就可以直接用這個物件了),比如:

1
2
3
4
5
my_rect1 = (100, 100, 200, 150)
my_rect2 = ((100, 100), (200, 150))
#上兩種為基礎方法,表示的矩形也是一樣的
my_rect3 = Rect(100, 100, 200, 150)
my_rect4 = Rect((100, 100), (200, 150))

一旦有了Rect物件,我們就可以對其做很多操作,比如調整位置和大小,判斷一個點是否在其中等等。以後會慢慢接觸到,求知慾旺盛的可以在http://www.pygame.org/docs/ref/rect.html中找到Rect的詳細資訊。

剪裁(Clipping)

通常遊戲的時候你只需要繪製螢幕的一部分。比如魔獸上面是選單,下面是操作皮膚,中間的小兵和英雄打的不可開交時候,上下的部分也是保持相對不動的。為了實現這一點,surface就有了一種叫裁剪區域(clipping area)的東西,也是一個矩形,定義了哪部分會被繪製,也就是說一旦定義了這個區域,那麼只有這個區域內的畫素會被修改,其他的位置保持不變,預設情況下,這個區域是所有地方。我們可以使用set_clip來設定,使用get_clip來獲得這個區域。

下面幾句話演示瞭如何使用這個技術來繪製不同的區域:

 

1
2
3
4
5
6
screen.set_clip(0, 400, 200, 600)
draw_map()
#在左下角畫地圖
screen.set_clip(0, 0, 800, 60)
draw_panel()
#在上方畫選單皮膚

子表面(Subsurfaces)

Subsurface就是在一個Surface中再提取一個Surface,記住當你往Subsurface上畫東西的時候,同時也向父表面上操作。這可以用來繪製圖形文字,儘管pygame.font可以用來寫很不錯的字,但只是單色,遊戲可能需要更豐富的表現,這時候你可以把每個字母(中文的話有些吃力了)各自做成一個圖片,不過更好的方法是在一張圖片上畫滿所有的字母。把整張圖讀入,然後再用Subsurface把字母一個一個“摳”出來,就像下面這樣:

 

1
2
3
4
my_font_image = Pygame.load("font.png")
letters = []
letters["a"] = my_font_image.subsurface((0,0), (80,80))
letters["b"] = my_font_image.subsurface((80,0), (80,80))

填充Surface

填充有時候可以作為一種清屏的操作,把整個surface填上一種顏色:

1
screen.fill((0, 0, 0))

同樣可以提供一個矩形來制定填充哪個部分(這也可以作為一種畫矩形的方法)。

設定Surface的畫素

我們能對Surface做的最基本的操作就是設定一個畫素的色彩了,雖然我們基本不會這麼做,但還是要了解。set_at方法可以做到這一點,它的引數是座標和顏色,下面的小指令碼會隨機的在螢幕上畫點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pygame
from pygame.locals import *
from sys import exit
from random import randint
 
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
 
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
 
    rand_col = (randint(0, 255), randint(0, 255), randint(0, 255))
    #screen.lock()    #很快你就會知道這兩句lock和unlock的意思了
    for _ in xrange(100):
        rand_pos = (randint(0, 639), randint(0, 479))
        screen.set_at(rand_pos, rand_col)
    #screen.unlock()
 
    pygame.display.update()

獲得Surface上的畫素

set_at的兄弟get_at可以幫我們做這件事,它接受一個座標返回指定座標點上的顏色。不過記住get_at在對hardware surface操作的時候很慢,而全屏的時候總是hardware的,所以慎用這個方法!

鎖定Surface

當Pygame往surface上畫東西的時候,首先會把surface鎖住,以保證不會有其它的程式來干擾,畫完之後再解鎖。鎖和解鎖時自動發生的,所以有時候可能不那麼有效率,比如上面的例子,每次畫100個點,那麼就得鎖解鎖100次,現在我們把兩句註釋去掉,再執行看看是不是更快了(好吧,其實我沒感覺出來,因為現在的機器效能都不錯,這麼點的差異還不太感覺的出來。不過請相信我~複雜的情況下會影響效率的)?

當你手動加鎖的時候,一定不要忘記解鎖,否則pygame有可能會失去響應。雖然上面的例子可能沒問題,但是隱含的bug是我們一定要避免的事情。

Blitting

blit的的中文翻譯給人摸不著頭腦的感覺,可以譯為位塊傳送(bit block transfer),其意義是將一個平面的一部分或全部圖象整塊從這個平面複製到另一個平面,下面還是直接使用英文。

blit是對錶面做的最多的操作,我們在前面的程式中已經多次用到,不多說了;blit的還有一種用法,往往用在對動畫的表現上,比如下例透過對frame_no的值的改變,我們可以把不同的幀(同一副圖的不同位置)畫到螢幕上:

1
screen.blit(ogre, (300, 200), (100 * frame_no, 0, 100, 100))

這次東西真是不少,打完脖子都酸了……

很多以前的程式中已經出現,看完這部分才能算是真正瞭解。影像是遊戲至關重要的一部分,值得多花時間,下一次講解繪製圖形~


相關文章