《零基础入门学习Python》第083讲:Pygame:提高游戏的颜值3

我们说过,图像是特定像素的组合,而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").convert_alpha() #左图
turtle2 = pygame.image.load("turtle.png").convert() #中图
turtle3 = pygame.image.load("turtle.png") #右图

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 对透明度的处理。

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("turtle.jpg").convert()
background = pygame.image.load("background.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() 把所有的白丝变为透明,来看一下效果:

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("turtle.jpg").convert()
background = pygame.image.load("background.jpg").convert()
position = turtle.get_rect()
position.center = width // 2, height // 2

#############################################################
#试图使用 set_colorkey() 把所有的白丝变为透明
turtle.set_colorkey((255, 255, 255))
#############################################################

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_alpha() 方法来调节整个图像的透明度,我们把透明度调节为200,来看一下效果:

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("turtle.jpg").convert()
background = pygame.image.load("background.jpg").convert()
position = turtle.get_rect()
position.center = width // 2, height // 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(background, (0, 0))
    screen.blit(turtle, position)
    
    pygame.display.flip()
    
    clock.tick(30)

我们看到整个图片都变得微微透明了,但是这个把背景也带上了,我们就想要小乌龟变透明,不想要白色边框。

我们把两种方法混合使用:

#############################################################
#试图使用 set_colorkey() 把所有的白丝变为透明
turtle.set_colorkey((255, 255, 255))

#用 set_alpha() 方法来调节整个图像的透明度为200
turtle.set_alpha(200)
#############################################################

效果依然不优秀。

我们再来试试 png,

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("turtle.png").convert_alpha()
background  = pygame.image.load("background.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)

由于我们这个 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)

显然是四元组(R,G,B,A),前面3个值表示 RGB颜色,最后的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)
#############################################################

卧槽,效果还是不优秀啊。而且就算效果优秀了,你这样一个一个像素的来计算透明度,效率未免也太低了吧,那怎么办?

没问题,程序是死的,人是活的,我这里教大家一个新技能来 搞定这个问题:

先看一下解决方案,我们再来分析:

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("turtle.png").convert_alpha()
background  = pygame.image.load("background.jpg").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)

这是如何实现的呢?我们来逐句分析一下:

我们封装了一个名为 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)

温馨提示:这虽然是一个小技巧,但是非常实用。大家好好理解一下。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值