1. 程式人生 > >用Python和Pygame寫遊戲-從入門到精通(6)學習筆記

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

雖然是基礎,這裡還是要羅嗦一下,之前說的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:

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

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

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

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

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

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

轉換Surfaces

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

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 *就可以直接用這個物件了),比如:

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))

剪裁(Clipping)

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

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

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把字母一個一個“摳”出來,就像下面這樣:

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填上一種顏色:

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

可以用整個白屏填充螢幕,達到清屏的效果。

設定Surface的畫素

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

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))
    for _ in xrange(100):
        rand_pos = (randint(0, 639), randint(0, 479))
        screen.set_at(rand_pos, rand_col)

    pygame.display.update()

效果圖:


獲得Surface上的畫素

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

get_at:在給定畫素處返回RGBA顏色值的副本。 如果Surface沒有每個畫素的alpha,那麼alpha值將始終為255(不透明)。 如果畫素位置在Surface的區域之外,則會引發IndexError異常。
逐個獲取和設定畫素通常太慢而無法用於遊戲或實時情況。 最好使用像blit,fill和draw方法一樣對許多畫素進行操作的方法 - 或者使用surfarray / PixelArray。

該功能將根據需要臨時鎖定和解鎖表面。

set_at:為單個畫素設定RGBA或對映的整數顏色值。 如果Surface沒有每個畫素的alpha,那麼alpha值將被忽略。 設定“表面”區域之外或“表面”裁剪之外的畫素將不起作用。
逐個獲取和設定畫素通常太慢而無法用於遊戲或實時情況。

該功能將根據需要臨時鎖定和解鎖表面。

鎖定Surface

當Pygame往surface上畫東西的時候,首先會把surface鎖住,以保證不會有其它的程序來干擾,畫完之後再解鎖。鎖和解鎖時自動發生的,所以有時候可能不那麼有效率,比如上面的例子,每次畫100個點,那麼就得鎖解鎖100次

Blitting

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

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

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

這一節主要把之前出現過的影象,surface之類的程式碼問題都做了一個詳細的解釋。