如何绘制表面(图像,字体)?以及颜色混合模式,剪辑区域,锁定等问题
关注 1421
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 中绘制表面视频演示 YouTube如何在 Pygame 中绘制表面视频演示 Bilibili
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 中转换像素格式,提高渲染效率视频演示 YouTube如何在 Pygame 中转换像素格式,提高渲染效率视频演示 Bilibili
如果一个表面对象需要被多次的绘制,那么可以事先转换像素格式,使其与需要绘制到的表面的像素格式相同。
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 中绘制图像和文本视频演示 Bilibili
事实上,在 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 中绘制表面·YouTube如何在 Pygame 中绘制表面·Bilibili
如何在 Pygame 中绘制图像和文本·YouTube如何在 Pygame 中绘制图像和文本·Bilibili
如何在 Pygame 中转换像素格式,提高渲染效率·YouTube如何在 Pygame 中转换像素格式,提高渲染效率·Bilibili