如何繪製表面(影像,字型)?以及色彩混合模式,裁剪區域,鎖定等問題
訂閱 375
Pygame 中的色彩混合模式
在 Pygame 中,如果Surface
物件支援透明度,那麽在繪製擁有透明度的色彩時,混合模式將決定色彩的顯示效果,即被繪製的色彩如何與相同位置的原有色彩進行計算。如果原有色彩沒有透明度或者表面物件不支援透明度,那麽即便設定了混合模式也不會生效。
pygame
模組提供了以下變數,可用於在函式或方法中設定色彩的混合模式,BLEND_ADD
,BLEND_SUB
,BLEND_MULT
,BLEND_MIN
,BLEND_MAX
,BLEND_RGBA_ADD
,BLEND_RGBA_SUB
,BLEND_RGBA_MULT
,BLEND_RGBA_MIN
,BLEND_RGBA_MAX
,BLEND_RGB_ADD
,BLEND_RGB_SUB
,BLEND_RGB_MULT
,BLEND_RGB_MIN
,BLEND_RGB_MAX
,BLEND_PREMULTIPLIED
,BLEND_ALPHA_SDL2
。
為表面填入色彩
Surface
物件的fill
方法,可用於為遊戲表面的指定區域填入某種色彩,並指定混合模式。
fill(color, rect, special_flags)
- color 參數
color
參數是一個包含色彩資訊的物件,該物件的書寫格式與Color
物件的建構子的rgbvalue
參數類似。- rect 參數
rect
參數是一個包含矩形資訊的物件,決定了表面物件的一些特性,該物件的書寫格式與Rect
物件的建構子的single_arg
參數類似。- special_flags 參數
special_flags
參數是色彩的混合模式,你可以檢視色彩混合模式一段來了解更多。
色彩,矩形
關於Color
建構子的rgbvalue
參數,你可以檢視Pygame 中的 Color 物件一段。關於Rect
建構子的single_arg
參數,你可以檢視Pygame 中的 Rect 物件一段。
在下面的範例中,我們繪製了兩個矩形,其中一個透明,另一個不透明,他們有部分區域是重疊的,混合模式為色彩相加。
from pygame import Surface, SRCALPHA, BLEND_RGBA_ADD
# 建立支援透明度的表面,並設定表面的透明度
s = Surface((800, 600), SRCALPHA)
# 繪製一個不透明矩形
s.fill('#ff0000', [100, 100, 100, 100])
# 繪製一個透明矩形,與不透明矩形部分重疊
s.fill('#ffff0099', [150, 150, 100, 100], BLEND_RGBA_ADD)
# 建立遊戲視窗並繪製 s
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
display.flip()
import time
time.sleep(3)
在表面中繪製表面
Pygame 繪製 Surface 表面影片示範 YouTube
Surface
物件的blit
方法,可用於在遊戲表面中繪製另一個表面。
blit(source, dest, area=None, special_flags=0)
- source 參數
source
參數是需要被繪製的表面物件。- dest 參數
dest
參數表示了繪製的開始位置,他可以是依次包含 X 座標和 Y 座標的 Python 浮點數序列物件,或者是包含矩形資訊的物件(書寫格式與Rect
物件的建構子的single_arg
參數類似),矩形的左上角座標即為繪製的開始位置(矩形的大小不會起到任何作用)。- area 參數
area
參數是一個包含矩形資訊的物件,用於指定繪製source
參數所表示的表面的哪個區域,該物件的書寫格式與Rect
物件的建構子的single_arg
參數類似。- special_flags 參數
special_flags
參數是色彩的混合模式,你可以檢視色彩混合模式一段來了解更多。- 傳回值
blit
方法的傳回值是一個Rect
物件,表示目標表面中被實際繪製了內容的區域,該區域不會超出表面的裁剪區域。
Surface
物件的blits
方法,可用於在遊戲表面中繪製多個其他表面。
blits(blit_sequence, doreturn=1)
- blit_sequence 參數
blit_sequence
參數是一個包含數個元組的 Python 序列物件,每一個元組的形式均類似於(source,dest,area,special_flags)
,其中的source
,dest
,area
,special_flags
的含義和書寫格式與blit
方法的source
,dest
,area
,special_flags
參數相同。你可以省略元組中的元素
area
或special_flags
,因此,元組的形式也可以類似於(source,dest)
,(source,dest,area)
或(source,dest,special_flags)
。- doreturn 參數
如果
doreturn
參數等價於True
,那麽blits
方法將傳回實際繪製區域,否則將傳回None
。- 傳回值
blits
方法的傳回值是None
或一個包含數個Rect
物件的 Python 串列,Rect
物件表示目標表面中被實際繪製了內容的區域,該區域不會超出表面的裁剪區域。
色彩深度為 8 的 Pygame Surface 物件不支援色彩的透明度
如果Surface
物件的色彩深度為 8,那麽表面將不支援色彩的透明度,source
參數所表示的表面中的色彩的透明度將被忽略。
from pygame import Surface
# 建立一些表面物件
s1 = Surface([100, 100])
s1.fill(0xff0000, [0, 0, 100, 100])
s2 = Surface([50, 50])
s2.fill('green', [0, 0, 50, 50])
s3 = Surface([100, 100])
s3.fill('#0000ff', [0, 0, 100, 100])
# 建立遊戲視窗並繪製多個表面
from pygame import display
w = display.set_mode((800, 600))
w.blits([
(s1, (0, 0)),
(s2, (100, 0)),
# 只繪製 s3 的左上角區域
(s3, (150, 0), [0, 0, 25, 25])
])
display.flip()
# 等候 3 秒鐘
import time
time.sleep(3)
轉換表面的像素格式
Pygame 轉換像素格式,提高 Surface 表面的算繪效率影片示範 YouTube
如果一個表面物件需要被多次的繪製,那麽可以事先轉換像素格式,使其與需要繪製到的表面的像素格式相同。
Surface
物件的convert
方法,將根據給出的像素格式建立一個新的不支援透明度的表面,如果沒有給出任何參數,則采用遊戲視窗對應的表面的像素格式。
convert()
convert(surface)
convert(depth, flags=0)
convert(masks, flags=0)
- surface 參數
surface
參數是一個表面物件,將采用該表面物件的像素格式。- depth,flags,masks 參數
參數
depth
,flags
和masks
,與Surface
物件的建構子的同名參數的作用和書寫格式相同,分別表示表面的色彩深度,特性,以及用於色彩對映整數的遮罩。- 傳回值
convert
方法的傳回值是一個已經轉換了像素格式的新的Surface
物件。
與convert
方法類似,Surface
物件的convert_alpha
方法同樣會根據給出的像素格式建立一個新的表面,只不過該表面支援透明度。
convert_alpha()
convert_alpha(surface)
- surface 參數
surface
參數是一個表面物件,將采用該表面物件的像素格式。- 傳回值
convert_alpha
方法的傳回值是一個已經轉換了像素格式的新的Surface
物件。
表面
關於Surface
建構子的相關參數,你可以檢視Pygame 中的 Surface 物件一段。
在下面的範例中,雖然使用了變數SRCALPHA
,但表面物件cs
並不支援透明度,因此,第一個紅色正方形沒有透明效果。
# 匯入並建立遊戲視窗
from pygame import display, Surface, SRCALPHA
w = display.set_mode((800, 600))
# 建立支援透明度的表面物件,填入具有透明度的紅色正方形
s = Surface([100, 100], SRCALPHA)
s.fill('#ff000099', [0, 0, 100, 100])
# 轉換像素格式,新表面不支援透明度
cs = s.convert(16, SRCALPHA)
print(f'cs 特性:{cs.get_flags()}')
# 轉換像素格式,新表面支援透明度
cas = s.convert_alpha()
print(f'cas 特性:{cas.get_flags()}')
# 在遊戲視窗中繪製 cs 和 cas
w.blit(cs, (0, 0))
w.blit(cas, (100, 100))
display.flip()
import time
time.sleep(3)
在表面中繪製影像和文字(字型)
Pygame 繪製影像和文字影片示範 YouTube
事實上,在 Pygame 中無論是影像還是文字,最終只能以Surface
表面物件的形式被繪製。對於影像,在使用image
模組載入影像檔案後,你將獲得已經繪製了影像的表面物件,對於文字,你需要首先建立Font
物件,然後使用字型物件的render
方法,將指定的文字轉換為一個表面物件。
render(text, antialias, color, background=None)
- text 參數
text
參數是包含了需要繪製的文字的字串。- antialias 參數
如果
antialias
參數等價於True
,那麽被繪製的文字將擁有反鋸齒效果。- color 參數
color
參數表示文字的色彩,其書寫格式與Color
物件的建構子的rgbvalue
參數類似。- background 參數
background
參數表示文字的背景色彩(預設為None
,背景透明),其書寫格式與Color
物件的建構子的rgbvalue
參數類似。- 傳回值
render
方法的傳回值是繪製了文字的Surface
物件。
Font 物件的 render 方法不會處理字串中的跳脫序列
Font
物件的render
方法不會處理字串中的跳脫序列,比如,換行字元\n
和定位字元\t
,他們可能會被表示為一個或數個空心方塊,這意味著要繪製多行文字可能需要多次呼叫render
方法。
# 匯入模組並進行初始化
from pygame import font, image
font.init()
# 使用 Pygame 預設字型繪製文字 Hello
f = font.Font(size=30)
ts = f.render('Hello', True, [2, 255, 0], '#ffffff')
# 載入影像檔案
img = image.load('candy.png')
# 建立遊戲視窗並繪製 ts 和 img
from pygame import display
w = display.set_mode([800, 600])
w.blit(ts, (50, 50))
w.blit(img, (100, 100))
display.flip()
import time
time.sleep(3)
取得和設定表面的裁剪區域
每一個 Pygame 的表面物件都擁有一個裁剪區域,位於裁剪區域以外的像素是不可修改的,預設情況下,裁剪區域等同於表面自身,這表示你可以為表面中的任意像素設定色彩。
Surface
物件的get_clip
方法,可用於取得表面物件的裁剪區域。
get_clip()
- 傳回值
get_clip
方法的傳回值是表示表面裁剪區域的Rect
物件。
Surface
物件的set_clip
方法,可用於設定表面物件的裁剪區域。
set_clip(rect)
- rect 參數
rect
參數是一個包含矩形資訊的物件,該物件用於指定表面的裁剪區域,其書寫格式與Rect
物件的建構子的single_arg
參數類似。如果
rect
參數為None
,那麽裁剪區域將等同於表面自身。
在下面的範例中,白色的正方形被設定為了表面的裁剪區域,而之後繪製的綠色正方形並沒有全部顯示,因為他的一部分在裁剪區域之外,當我們重新將裁剪區域設定為表面自身後,藍色正方形被完整的繪製。
from pygame import Surface
s = Surface([800, 600])
# 繪製一個白色的正方形並將其設定為裁剪區域
s.fill('white', ((100, 100), (100, 100)))
s.set_clip((100, 100), (100, 100))
print(f'目前裁剪區域:{s.get_clip()}')
# 繪製一個與白色正方形部分重疊的綠色正方形
s.fill('#00ff00', (150, 150, 100, 100))
# 將整個表面設定為裁剪區域
s.set_clip(None)
print(f'目前裁剪區域:{s.get_clip()}')
# 繪製一個與白色正方形部分重疊的藍色正方形
s.fill('#0000ff', [50, 50, 100, 100])
# 建立遊戲視窗並繪製 s
from pygame import display
w = display.set_mode((800, 600))
w.blit(s, (0, 0))
display.flip()
import time
time.sleep(3)
目前裁剪區域:<rect(100, 100, 100, 100)>
目前裁剪區域:<rect(0, 0, 800, 600)>
Surface 物件的裁剪區域不會超出表面自身表示的範圍
當set_clip
方法的rect
參數指示一個超出表面自身範圍的裁剪區域時,該裁剪區域將被調整(超出表面自身範圍的區域將不被考慮),以使其完全處於表面所表示的範圍之內。
from pygame import Surface
s = Surface([100, 100])
# 裁剪區域超出了表面的大小
s.set_clip((0, 0, 110, 120))
print(f'目前裁剪區域:{s.get_clip()}')
目前裁剪區域:<rect(0, 0, 100, 100)>
鎖定和解除鎖定表面
Surface
物件的lock
和unlock
方法,可用於鎖定和解除鎖定表面。當一個表面被鎖定時,Pygame 將不能對其進行繪製和管理,這意味著該表面無法顯示在遊戲中,除非他被解除鎖定,正因如此,你不應該讓一個Surface
物件長時間處於鎖定狀態。
lock()
unlock()
Surface
物件的mustlock
方法,可用於判斷是否必須鎖定表面,在存取與表面像素相關的資料之前,該方法幾乎不會被使用,因為 Pygame 會自行管理表面的鎖定和解除鎖定。
mustlock()
- 傳回值
如果
mustlock
方法傳回True
,那麽表示表面需要鎖定。
Surface
物件的get_locked
方法,可用於判斷表面是否已經被鎖定。
get_locked()
- 傳回值
如果
get_locked
方法傳回True
,那麽表示表面已經被鎖定。
Surface
物件的get_locks
方法,可用於取得已經被鎖定的表面,通常是指表面自身,但也可能是該表面的子表面。
get_locks()
- 傳回值
get_locks
方法傳回一個 Python 元組,元組中包含了被鎖定的表面物件。這裏需要說明的是,如果你多次呼叫Surface
物件的lock
方法,那麽Surface
物件可能會重複出現在元組中。
Surface 物件通常被自動鎖定或解除鎖定
當需要對遊戲表面進行某種讀取或寫入時,一些方法或函式將自動鎖定或解除鎖定Surface
表面物件,因此,需要主動呼叫lock
方法的情況並不多,除非有大量鎖定和解除鎖定表面的操作,比如,對表面中的每個像素逐一進行讀取或修改。
在下面的範例中,由於我們鎖定了表面物件s1
,因此,將其繪製到表面物件s2
會引發例外狀況pygame.error: Surfaces must not be locked during blit
。
from pygame import Surface
s1 = Surface([800, 600])
s1.fill('white', [0, 0, 100, 100])
print(f'是否需要鎖定:{s1.mustlock()}')
# 鎖定表面兩次
s1.lock()
s1.lock()
print(f'是否已經鎖定:{s1.get_locked()}')
print(f'已經存在的鎖定:{s1.get_locks()}')
s2 = Surface([800, 600])
# ERROR 無法繪製被鎖定的表面
s2.blit(s1, [0, 0])
pygame.error: Surfaces must not be locked during blit
原始碼
講解影片
Pygame 繪製 Surface 表面·YouTube
Pygame 繪製影像和文字·YouTube
Pygame 轉換像素格式,提高 Surface 表面的算繪效率·YouTube