图像是特定像素的组合,而Surface 对象是Pygame里面对图像的描述,在Pygame里面到处都是Surface 对象,set_mode() 返回的是一个Surface对象,在界面上打印文字也需要先把文字渲染成 Surface 对象,然后再贴上去,小蛇在上面爬呀爬,其实就是不断调整Surface对象上的一些特定的像素的位置,把小蛇所在位置的像素进行移动,就是小蛇在上面爬,就是调用 blit() 方法。imag.load() 方法载入图像并会返回一个 Surface 对象,我们此前都是直接拿来用,并没有进行任何转换,这样子就是效率比较低的做法。如果你希望你的Pygame可以尽可能高效的处理你的图片,你应该在图片载入之后立刻调用 convert() 方法进行转换。
如:bg = pygame.image.load(“bg.jpg”).convert()
事实上,游戏都是由各种不同的图片组成的,例如说:背景是一个图片、里面的主角是图片、反派也是图片、还有路人甲乙丙都是图片。你总不可能用一个圆形或者矩阵画一个主角吧。我们只能在现实中先用Photoshop 画一个惟妙惟肖的主角,然后贴进去,然后使用 blit() 方法让它移动。
有的人就很好奇了,不是说 image.load() 返回一个 Surface 对象吗,那还转换个毛线啊,这里的转换只是像素格式的转换,而不是说转换为Surface 对象,因为image.load() 载入之后就是一个 Surface 对象,但是我们载入之后(例如我们载入一个JPG格式的图片,JPG图片也是由像素组成的,而这些像素都是有颜色的,另外我们还可以将这个JPG图片保存为PNG,GIF格式,你会发现尺寸会发生改变了,这是因为里面的像素格式发生改变了,也就是说它组合这些像素,把它描述的形式发生改变了),Surface 也有它自己的像素格式,所以我们这里的转换指的就是图片的像素格式的转换。
如果我们没有在 image.load() 之后立刻对它进行转换,但是转换非常重要,Pygame也会你在 调用 blit() 方法时自动进行转化,就是将一个图片贴到另一个图片之上的时候,因为两个图片要进行复制拷贝的操作,它们的像素格式必须相同,因此在每次 blit() 的时候,它都会强制转换一次,这样子效率就相当低了,与其让它每次循环去转换一次,我们还不如在载入时调用 convert() 方法转换为 Surface 的像素格式。
虽然现在的CPU速度都很快,这一点细微的差别你可能看不出来,但是我们都希望我们的程序小一点、效率高一点。今后我们都会在 image.load() 之后立刻调用 convert()。
还有一个就是 convert_alpha() 方法:
如:turtle = pygame.image.load(“python.png”).convert_alpha()
这两个方法有什么区别呢?一般情况下,我们使用 RGB 来描述一个颜色,然而在游戏开发中,我们常常用到的是 RGBA (RGBA是代表Red(红色) Green(绿色) Blue(蓝色)和 Alpha的色彩空间)来描述。Alpha 通道是用来表示透明度的,A 占用一个字节,也就是8位(0-255,256种层次),用序号来索引,就是0-255,0表示完全透明,255表示完全不透明。
我们都知道,image.load() 支持多种格式的图片导入,例如 gif、jpg、png等,这些都是当前流行的图片格式,对于包含 Alpha 通道的图片,我们就要用 convert_alpha() 方法来转换格式了,其它的就用 conmvert() 方法。
我们也知道,jpg 格式的图片是不包含 Alpha 通道的,因为它不能来表示透明。我们在做图片的时候,我们知道,两种常用的 透明格式就是 png和 gif 格式,而gif 还支持动图,动图在Pygame 里面是不能解析的,一般我们在Pygame 里面做的图片都是以 png 图片为主,因为 jpg 是有损的,你放大缩小它会损失精度,png 是无损压缩。
jpg 是不支持透明的,所以我们载入这类图片就用 convert();而 png 是支持透明的,所以载入就用 convert_alpha()。
大家可以看一下下面两张图片:
左边是 png 格式的原图,是透明背景的,当我另存为 jpg 格式时,背景就不透明了。
如果你载入左边的透明 png 图片,使用 convert_alpha() 方法转换和使用 convert() 方法转换,比较一下。
import pygame import sys pygame.init() size = width, height = 900, 300 bg = (0, 255, 0) #为了便于区分,背景设为金色 clock = pygame.time.Clock() screen = pygame.display.set_mode(size) pygame.display.set_caption("Python Demo") turtle1 = pygame.image.load("turtle.png") #左图 turtle2 = pygame.image.load("turtle.png").convert() #中图 turtle3 = pygame.image.load("turtle.png").convert_alpha() #右图 position1 = turtle1.get_rect() position1.center = width // 6, height // 2#居左显示 position2 = turtle2.get_rect() position2.center = width // 2, height // 2#居中显示 position3 = turtle3.get_rect() position3.center = 5 * width // 6, height // 2#居右显示 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() screen.fill(bg) screen.blit(turtle1, position1) screen.blit(turtle2, position2) screen.blit(turtle3, position3) pygame.display.flip() clock.tick(30)
这貌似跟正常出的结果不一样,这可能是颜色本身的问题。(需要进一步研究~)
Pygame 支持三种透明度类型:colorkeys,surface alphas 和 pixel alphas(温馨提示:colorkeys 是指设置图像中的某个颜色值为透明(比如说上面的乌龟有很多种绿色,我把其中一种绿色变为透明,那么在这个图片里面,与这个颜色相同的部分都不见了,取而代之的是背景的颜色,因为透明事实上就是把背景显示出来。),surface alphas 是调整整个图像的透明度,pixel alphas 则是独立设置图像中每一个像素的透明度)。
png 就是一个 piexl alphas,所以它每个像素都有一个 Alpha 通道,指定这个像素是否要变透明,透明度是多少。
surface alphas 可以和 colorkeys 混合使用,而 pixel alphas 不能和其他两个混合。
说起来很复杂,其实说白了,convert() 方法转换出来的 就支持 surface alphas 可以和 colorkeys 设置透明度,而且他们是可以混合设置的。而 convert_alpha() 方法转换之后呢,就只支持 piexl alphas ,也就是说这个图像本身每个像素就带有Alpha 通道,我们载入一个带有 Alpha 通道的图片,我们会看到有一部分是透明的,就像我们上面的小乌龟,它的背景就是透明的。
我们来做一下实验:
我们这里有两张图片,一种是 jpg,背景白色;一张是 png,背景透明。
我们首先载入这张 jpg 图片,为了更好区分,我们加了一个背景。
import pygame import sys from pygame.locals import * pygame.init() size = width, height = 640, 480 bg = (0, 0, 0) clock = pygame.time.Clock() screen = pygame.display.set_mode(size) pygame.display.set_caption("Python Demo") turtle = pygame.image.load("wagger.png").convert() background = pygame.image.load("green_grue.jpg").convert() position = turtle.get_rect() position.center = width // 2, height // 2 while True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() screen.blit(background, (0, 0)) screen.blit(turtle, position) pygame.display.flip() clock.tick(30)
现在分别使用 set_colorkey() 把所有的白丝变为透明,turtle.set_alpha(200)把整幅图像变透明,以及同时使用两者,来看一下效果:
import pygame import sys from pygame.locals import * pygame.init() size = height,width = 640,480 bg=(0,0,0) clock = pygame.time.Clock() screen = pygame.display.set_mode(size) pygame.display.set_caption("遇见你真好") turtle = pygame.image.load("wagger.png").convert() bg = pygame.image.load("green_grue.jpg").convert() position =turtle.get_rect() position.center = height // 2,width // 2 ############################################################# #试图使用 set_colorkey() 把所有的白丝变为透明 turtle.set_colorkey((255, 255, 255)) ############################################################# #用 set_alpha() 方法来调节整个图像的透明度为200 turtle.set_alpha(200) ############################################################# while True: for event in pygame.event.get(): if event.type == quit: sys.exit() screen.blit(bg,(0,0)) screen.blit(turtle,position) pygame.display.flip() clock.tick(30)
仔细观察我们可以发现,set_colorkey()结果并不优秀,因为边缘并不是纯白色的,结果并不是我们想要的,并不理想。 set_alpha() 方法将整个图片都变得微微透明了,但是这个把背景也带上了,我们就想要小乌龟变透明,不想要白色边框。两种混合使用的话,效果依然不优秀。
wagger.jpg的图像没有上述wagger.png图像的效果好。由于我们这个 png 是带 Alpha 通道的,而且我们在做这个图片的时候,已经把它的背景给扣成透明的了,下面的阴影不透明,但是不影响美观(是故意做出的效果)。我们想把整个小乌龟调为透明度 200,让小乌龟有一种隐身的既视感。但是我们说这个是 piexl alphas,也就是每个像素都有一个 Alpha 通道,因此我们不能使用 turtle.set_alpha(200) 方法,我们可以使用 get_at() 方法来获得单个像素的透明度,并且用 set_at() 方法来修改它。
这样可以在获取单个像素的透明度的情况下,
############################################################# #尝试是否能够获得 单个像素的透明度 print(turtle.get_at(position.center)) #获取中间那个像素的颜色 ############################################################# >>> ============================== RESTART ============================== pygame 1.9.4 Hello from the pygame community. https://www.pygame.org/contribute.html (130, 131, 26, 255)
利用循环把透明度设置为200。
############################################################# for i in range(position.width): for j in range(position.height): temp = turtle.get_at((i, j)) if temp[3] != 0: temp[3] = 200 turtle.set_at((i, j), temp) #############################################################
结果如下:
PS:上图有一个小瑕疵,不知道你们看出来了没有,就是青蛙不能居中显示,具体的原因和解决方法有待研究。
效果还是不优秀啊。而且就算效果优秀了,你这样一个一个像素的来计算透明度,效率未免也很低
没问题,程序是死的,人是活的,我这里教大家一个新技能来 搞定这个问题:
先看一下解决方案,我们再来分析:
import pygame import sys from pygame.locals import * pygame.init() size = width, height = 800, 600 bg = (0, 0, 0) clock = pygame.time.Clock() screen = pygame.display.set_mode(size) pygame.display.set_caption("Python Demo") turtle = pygame.image.load("wagger.png").convert_alpha() background = pygame.image.load("white.png").convert() position = turtle.get_rect() position.center = width // 2, height // 2 ############################################################# def blit_alpha(target, source, location, opacity): x = location[0] y = location[1] temp = pygame.Surface((source.get_width(), source.get_height())).convert() temp.blit(target, (-x, -y )) temp.blit(source, (0, 0)) temp.set_alpha(opacity) target.blit(temp, location) ############################################################# while True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() screen.blit(background, (0, 0)) ############################################################ #screen.blit(turtle, position) blit_alpha(screen, turtle, position, 200) ############################################################ pygame.display.flip() clock.tick(30)
PS:这效果又跟平常的不一样,需要课后研究深入了解一下。
据说,效果是这样的:
这是如何实现的呢?我们来逐句分析一下:
我们封装了一个名为 blit_alpha() 的函数,下面我们就不要调用 blit() 方法了,而是调用 blit_alpha(),因为我们在函数的末尾 调用了 blit() 方法。
在函数中,我们首先创建一个 Surface ,传进来的是一个带 Alpha 的,我们将其变为不带 Alpha 的,其实我们只是需要它的一个矩形区域,
即:temp = pygame.Surface((source.get_width(), source.get_height())).convert()
temp 就是下图种黑色矩形的位置,其实就是小乌龟图片的覆盖区域范围。
然后我们在上边绘制背景
即:temp.blit(target, (-x, -y ))
为什么是(-x, -y)呢?
我们知道, x = location[0],y = location[1],而形参 location对应的实参是 position,就是小乌龟图像的位置,其实(x, y)就是下图的A点,这个位置是相对于顶点B(0,0)(对于screen 来说,B就是(0,0))来说的,而 temp.blit()是相对于小乌龟图像的来粘贴背景,所以B相对于A点来说,就是(-x,-y)。
然后我们就在A的位置贴上背景透明的 小乌龟图片
即:temp.blit(source, (0, 0))
然后我们将整个 temp 区域设为 透明度 200,其实现在的整个 temp 区域(Surface 对象)就是A区域大小,然后背景是该区域的草地,主角是小乌龟的一幅图片。如图所示:(这个就是目前的temp了)
我们将这个图片 透明度设为 200。
即:temp.set_alpha(opacity) #opacity=200
这就巧妙的避开了 带Alpha通道的Surface 不能调用set_alpha()方法的问题。因为现在的temp 是不带Alpha通道的。
然后我们就把这个透明度为200的图片贴在背景屏幕上,
即:target.blit(temp, location)