如何绘制表面(图像,字体)?以及颜色混合模式,剪辑区域,锁定等问题
关注 1800
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