鱼C论坛

 找回密码
 立即注册
查看: 1773|回复: 4

[已解决]小乌龟的透明度设置

[复制链接]
发表于 2022-7-2 21:55:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
# 透明度的设置

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('FishC Demo')


turtle = pygame.image.load(r'd:\\work\turtle.png').convert_alpha()
background = pygame.image.load(r'd:\\work\\grass.jpg').convert()
position = turtle.get_rect()
position.center = width // 2, height // 2
print(position, '\n上:', position.top, '\n下:', position.bottom, '\n左:', position.left, '\n右:', position.right, '\n宽度:', position.width, '\n高度:', position.height, '\n中心点:', position.center)



def blit_new(t, s, l, o):  # t-target, s-source, l-location, o-opacity

    #x = l[0]

    #y = l[1]

    x, y = l[0], l[1]

    temp = pygame.Surface((s.get_width(), s.get_height())).convert()

    temp.blit(t, (-x, -y))

    temp.blit(s, (0, 0))

    temp.set_alpha(o)

    t.blit(temp, l)
   


while True:
   
    for event in pygame.event.get():
        if event.type == QUIT:
            sys.exit()


    #screen.fill(bg)
    screen.blit(background, (0, 0))
    blit_new(screen, turtle, position, 200)
   
   

    pygame.display.flip()

    clock.tick(30)

------------------------------------------
Windows 10 专业版 | Python 3.7.6
------------------------------------------

【我的问题】

1、【代码分析】自定义函数blit_alpha()实现步骤:
① 首先创建一个不带Alpha通道的小乌龟
② 然后将小乌龟所在位置的背景覆盖上去
③ 此刻temp得到的是一个尺寸与小乌龟的尺寸一样大小、上面绘制着背景的Surface对象
④ 将带Alpha通道的小乌龟覆盖上去
⑤ 由于temp是不带Alpha通道的Surface对象,因此可以使用set_alpha()方法设置整个图片的透明度---此时的temp是否包含小乌龟?
⑥ 最后将设置好透明度的temp“贴”到指定位置上---此时的temp是否包含小乌龟?

2、为什么screen.blit(background, (0, 0)),而blit_new(screen, turtle, position, 200)开头不需要screen.?

3、 temp.blit(t, (-x, -y))语句中,t(即target)是指什么?为什么是 (-x, -y)?

4、 temp.blit(s, (0, 0))语句中,s(即source)是指什么?为什么是 (0, 0)?

5、t.blit(temp, l)没看懂……

******************************

感谢大神不吝赐教,为新手解疑释惑。

赠人玫瑰,手有余香,好人一生平安!
最佳答案
2022-7-8 09:36:23
看得出来,你对新定义的blit_new函数非常不理解。我把你的代码下载下来仔细分析了一遍,现在给你解答吧。

你的第二个问题(2、为什么screen.blit(background, (0, 0)),而blit_new(screen, turtle, position, 200)开头不需要screen.?)放到最后回答,这个与blit_new的逻辑无关,是另一个方面的问题。

尽管用注释在后面说明了每个参数是什么意思,但blit_new这个函数的签名(def blit_new(t, s, l, o))太过简陋了,不如一开始就用有意义的参数名这样也有助于理解代码逻辑。
于是乎我把代码的参数名按照注释修改了过来并且增加了类型注释,逻辑完全没有修改:
def blit_new(
        target: pygame.Surface,
        source: pygame.Surface,
        location: tuple[int, int],
        opacity: int
) -> None:
    x, y = location[0], 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)
参数名后面的冒号以及pygame.Surface这部分叫做类型注释,用于说明参数的类型,不添加也没有影响;-> None表明这个函数的返回值是NoneType类型。
关于函数签名的修改介绍到这里接下来结合调用这个函数的时机来分析函数运行的逻辑。

在while循环中,函数是这样被调用的:blit_new(screen, turtle, position, 200)。
所以target参数对应的是screen;
source参数对应的是turtle;
location参数对应的是(turtle)的location;
opacity参数对应的是200。

进入函数内部再来分析它的运行过程:
x, y = location[0], location[1]
这行代码是获取location的x、y坐标。location是之前通过turtle.get_rect()获得的,也就是说它其实是一个Rect对象,记录了turtle的位置和尺寸
temp = pygame.Surface((source.get_width(), source.get_height())).convert()
由于source是一个Surface对象,所以调用的两个函数分别获取了Surface对象的宽度和高度,然后组成了一个元组,利用这个元组再创建一个Surface对象。
经过之前的参数分析我们知道source就是turtle,所以这行其实就是创建了一个和turtle一样大的Surface对象,最后的.convert()是为Surface对象增加了透明图层。
也就是说temp这个变量代表的是一个大小与turtle相同带有透明通道的Surface对象。
temp.blit(target, (-x, -y))
这行代码的意思是把targe绘制到temp的(-x, -y)位置。
我们知道此处的target是screen,temp是一个新建的大小与turtle相同的Surface对象;x、y分别表示turtle在screen上的左上角坐标。
如果不理解可以参考下面这张图片:

(-x, -y)表示把screen向左向上分别偏移x、y个单位,如此一来尽管screen完全盖住了temp,但是二者中心点重合。
理解这行代码之后下一行就相对简单一些了。
temp.blit(source, (0, 0))
把source绘制到temp的(0, 0)位置。因为source代表turtle,而且temp与turtle大小相同,所以就是让turtle覆盖到temp上。
此时我们来分析temp的构成,temp由三层Surface叠加而成:最下层是一个黑色的矩形框,大小与turtle相同;中间层是screen;最上层是turtle。三者合而为一。
temp.set_alpha(opacity)
这行代码是设置temp的透明度,取值范围是0-255,数字越大越透明。调用函数时透明度传入的是200,所以此时的opacity为200。
target.blit(temp, location)
最后这一行就是把temp绘制到target的location位置。bit这个函数的第二个参数需要一个坐标,而location这个Rect对象本身就存储了有关坐标的信息所以可以直接把它当坐标传给bit函数。
target是screen,temp是叠加而成的一个新的Surface对象,具有200的透明度。
location是turtle的位置信息,而且location的中心点坐标与screen的中心点坐标一样(前面有设置location.center = width//2, height//2),所以这行代码就是把temp绘制到screen的正中心。
由于temp具有透明度所以背景会透出来,turtle也会看起来有些透明效果。

以上就是blit_new函数的运行逻辑。还是建议以后写函数不要用过于简单的参数名,否则自己都看不懂参数表示什么含义,分析逻辑也就更难了。
代码是写给人看的,机器只看二进制码。

#################分割线###################

最后再来回答你的第二个问题,为什么有的函数开头有前缀screen. temp.而blit_new这个函数却没有?
这里涉及到了两个知识点:类与对象,命名空间。
类与对象不做解释,因为内容过多,你学完了这部分自然知道为啥要这么些了。
接下来我尝试用简单的语言介绍命名空间。
想象这样一种情景,你们班上有一个同学小王,隔壁班也有一个同学小王。所有人一起在操场上的时候如果老师要点名你们班的小王怎么办?
肯定不能直接喊:“小王同学出列”,更好的做法是“三班的小王同学出列”。老师给你们班所在的位置分配了一个名字,叫做三班。然后在这个区域内寻找小王同学,这样就避免了找到其他班级同名同学的问题。
更进一步我们可以把“三班的小王”这样写三班.小王,这就表示小王属于三班这个空间。类似地,Python也会帮我们把内存进行划分并命名。
turtle = pygame.image.load("turtle.png")这样一行代码的意思就是在内存中加载turtle.png这张图片并创建一个Surface对象,然后把Surface对象所在的这块内存空间命名为turtle。
这块空间还有其他一些东西,想要使用这块空间里面的东西时我们就可以采用这样的格式:turtle.xxx。
例如,调用get_rect方法获取大小位置信息可以这样写turtle.get_rect(),这表明get_rect这个方法只属于turtle这块内存。
类似地pygame.display.flip()也是类似的意思,pygame这块巨大的内存中划分出一块名为display的内存,在里面有个叫做flip的函数。

综上,我们可以把命名空间这个概念这样理解:把一块内存空间进行命名,然后通过形如aaa.bbb.ccc这样的形式使用内存中的函数、变量等等,这样的形式也可以表明它们之间的层级关系或者说所属关系。
以aaa.bbb.ccc为例,bbb直接属于aaa,ccc直接属于bbb,间接属于aaa。

到此为止,我们了解了为什么有些函数前面具有前缀。再来说说为什么有些函数前面没有前缀,在一个Python文件内,当变量、函数等没有任何缩进时,我们说它们在级别上属于最顶级的,这种顶级的名称是不需要加前缀的。blit_new函数定义时是顶格的,所以

以上就是针对你的所有问题的回答,前前后后编写超过一个小时,如果觉得有用请给个最佳答案,谢谢~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-7-3 10:06:48 | 显示全部楼层
论坛都没有大神解答了,小甲鱼得亲自出面了看来
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-7-8 09:36:23 | 显示全部楼层    本楼为最佳答案   
看得出来,你对新定义的blit_new函数非常不理解。我把你的代码下载下来仔细分析了一遍,现在给你解答吧。

你的第二个问题(2、为什么screen.blit(background, (0, 0)),而blit_new(screen, turtle, position, 200)开头不需要screen.?)放到最后回答,这个与blit_new的逻辑无关,是另一个方面的问题。

尽管用注释在后面说明了每个参数是什么意思,但blit_new这个函数的签名(def blit_new(t, s, l, o))太过简陋了,不如一开始就用有意义的参数名这样也有助于理解代码逻辑。
于是乎我把代码的参数名按照注释修改了过来并且增加了类型注释,逻辑完全没有修改:
def blit_new(
        target: pygame.Surface,
        source: pygame.Surface,
        location: tuple[int, int],
        opacity: int
) -> None:
    x, y = location[0], 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)
参数名后面的冒号以及pygame.Surface这部分叫做类型注释,用于说明参数的类型,不添加也没有影响;-> None表明这个函数的返回值是NoneType类型。
关于函数签名的修改介绍到这里接下来结合调用这个函数的时机来分析函数运行的逻辑。

在while循环中,函数是这样被调用的:blit_new(screen, turtle, position, 200)。
所以target参数对应的是screen;
source参数对应的是turtle;
location参数对应的是(turtle)的location;
opacity参数对应的是200。

进入函数内部再来分析它的运行过程:
x, y = location[0], location[1]
这行代码是获取location的x、y坐标。location是之前通过turtle.get_rect()获得的,也就是说它其实是一个Rect对象,记录了turtle的位置和尺寸
temp = pygame.Surface((source.get_width(), source.get_height())).convert()
由于source是一个Surface对象,所以调用的两个函数分别获取了Surface对象的宽度和高度,然后组成了一个元组,利用这个元组再创建一个Surface对象。
经过之前的参数分析我们知道source就是turtle,所以这行其实就是创建了一个和turtle一样大的Surface对象,最后的.convert()是为Surface对象增加了透明图层。
也就是说temp这个变量代表的是一个大小与turtle相同带有透明通道的Surface对象。
temp.blit(target, (-x, -y))
这行代码的意思是把targe绘制到temp的(-x, -y)位置。
我们知道此处的target是screen,temp是一个新建的大小与turtle相同的Surface对象;x、y分别表示turtle在screen上的左上角坐标。
如果不理解可以参考下面这张图片:

(-x, -y)表示把screen向左向上分别偏移x、y个单位,如此一来尽管screen完全盖住了temp,但是二者中心点重合。
理解这行代码之后下一行就相对简单一些了。
temp.blit(source, (0, 0))
把source绘制到temp的(0, 0)位置。因为source代表turtle,而且temp与turtle大小相同,所以就是让turtle覆盖到temp上。
此时我们来分析temp的构成,temp由三层Surface叠加而成:最下层是一个黑色的矩形框,大小与turtle相同;中间层是screen;最上层是turtle。三者合而为一。
temp.set_alpha(opacity)
这行代码是设置temp的透明度,取值范围是0-255,数字越大越透明。调用函数时透明度传入的是200,所以此时的opacity为200。
target.blit(temp, location)
最后这一行就是把temp绘制到target的location位置。bit这个函数的第二个参数需要一个坐标,而location这个Rect对象本身就存储了有关坐标的信息所以可以直接把它当坐标传给bit函数。
target是screen,temp是叠加而成的一个新的Surface对象,具有200的透明度。
location是turtle的位置信息,而且location的中心点坐标与screen的中心点坐标一样(前面有设置location.center = width//2, height//2),所以这行代码就是把temp绘制到screen的正中心。
由于temp具有透明度所以背景会透出来,turtle也会看起来有些透明效果。

以上就是blit_new函数的运行逻辑。还是建议以后写函数不要用过于简单的参数名,否则自己都看不懂参数表示什么含义,分析逻辑也就更难了。
代码是写给人看的,机器只看二进制码。

#################分割线###################

最后再来回答你的第二个问题,为什么有的函数开头有前缀screen. temp.而blit_new这个函数却没有?
这里涉及到了两个知识点:类与对象,命名空间。
类与对象不做解释,因为内容过多,你学完了这部分自然知道为啥要这么些了。
接下来我尝试用简单的语言介绍命名空间。
想象这样一种情景,你们班上有一个同学小王,隔壁班也有一个同学小王。所有人一起在操场上的时候如果老师要点名你们班的小王怎么办?
肯定不能直接喊:“小王同学出列”,更好的做法是“三班的小王同学出列”。老师给你们班所在的位置分配了一个名字,叫做三班。然后在这个区域内寻找小王同学,这样就避免了找到其他班级同名同学的问题。
更进一步我们可以把“三班的小王”这样写三班.小王,这就表示小王属于三班这个空间。类似地,Python也会帮我们把内存进行划分并命名。
turtle = pygame.image.load("turtle.png")这样一行代码的意思就是在内存中加载turtle.png这张图片并创建一个Surface对象,然后把Surface对象所在的这块内存空间命名为turtle。
这块空间还有其他一些东西,想要使用这块空间里面的东西时我们就可以采用这样的格式:turtle.xxx。
例如,调用get_rect方法获取大小位置信息可以这样写turtle.get_rect(),这表明get_rect这个方法只属于turtle这块内存。
类似地pygame.display.flip()也是类似的意思,pygame这块巨大的内存中划分出一块名为display的内存,在里面有个叫做flip的函数。

综上,我们可以把命名空间这个概念这样理解:把一块内存空间进行命名,然后通过形如aaa.bbb.ccc这样的形式使用内存中的函数、变量等等,这样的形式也可以表明它们之间的层级关系或者说所属关系。
以aaa.bbb.ccc为例,bbb直接属于aaa,ccc直接属于bbb,间接属于aaa。

到此为止,我们了解了为什么有些函数前面具有前缀。再来说说为什么有些函数前面没有前缀,在一个Python文件内,当变量、函数等没有任何缩进时,我们说它们在级别上属于最顶级的,这种顶级的名称是不需要加前缀的。blit_new函数定义时是顶格的,所以

以上就是针对你的所有问题的回答,前前后后编写超过一个小时,如果觉得有用请给个最佳答案,谢谢~
Snipaste_2022-07-08_08-40-25.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-7-8 10:50:02 | 显示全部楼层
太感谢大神耐心、详尽、细致的讲解,也希望版主能将此贴设为精华帖,以帮助更多不理解透明代码含义的初学者理解。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-7-11 09:37:20 | 显示全部楼层
本帖最后由 lzb1001 于 2022-7-11 10:26 编辑

细读了大神的详细指点,发现几个问题现提出如下:

1、原文:
在while循环中,函数是这样被调用的:blit_new(screen, turtle, position, 200)。
所以target参数对应的是screen;
source参数对应的是turtle;
location参数对应的是(turtle)的location;->此行中第二个location应为position才对吧?
opacity参数对应的是200。
---注意红色字体

2、原文:…这行代码是获取location的x、y坐标。location是之前通过turtle.get_rect()获得的,也就是说它其实是一个Rect对象,记录了turtle的位置和尺寸;…
---代码开头position = turtle.get_rect(),所以上面说的“location是之前通过turtle.get_rect()获得的”也就是:location是之前通过turtle.get_rect()获得的(即position),对吧?

3、原文:(-x, -y)表示把screen分别向左、向上偏移x、y个单位,如此一来尽管screen完全盖住了temp,但是二者中心点重合。
---这句感觉还是不理解或者说想不通

4、原文:…此时我们来分析temp的构成,temp由三层Surface叠加而成:最下层是一个黑色的矩形框,大小与turtle相同;中间层是screen;最上层是turtle。三者合而为一。…
---最下层是一个矩形框没错,为何是黑色?没看到有黑色的定义啊?

5、原文:…这行代码是设置temp的透明度,取值范围是0-255,数字越大越透明。调用函数时透明度传入的是200,所以此时的opacity为200。…
---数字越大越透明->刚测试了:如果设置为255则小乌龟完全不透明,如果设置为0则小乌龟完全透明(小乌龟此时完全不可见)。

6、原文:…经过之前的参数分析我们知道source就是turtle,所以这行其实就是创建了一个和turtle一样大的Surface对象,最后的.convert()是为Surface对象增加了透明图层。
也就是说temp这个变量代表的是一个大小与turtle相同且带有透明通道的Surface对象。…
---我感觉这个好像是不是说错了?.convert()方法应该是转换成不带alpha通道的格式,也就是说转换后的图片不带透明图层。退一步说,如果.convert()方法是为Surface对象增加了透明图层,也就是转换成带alpha通道的格式,后面如何能使用set_alpha()方法来设置temp的透明度呢?因为根据小甲鱼的教材,带Alpha通道的Surface不能调用set_alpha()方法。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-17 15:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表