Pygame开发如何避免新建对象时透明度叠加相乘的问题
问题根因
透明度叠加的bug完全来自共享透明面的无清理累积绘制逻辑:
- 你在
Place_circles里只初始化了1块全屏尺寸的透明面inner,后续每创建一个圆形,都会直接在这块共享面上绘制新的半透明内圈,全程没有做清空操作 - 每次绘制完新内圈后,你会把这块已经累计了所有历史半透明图形的
inner面整体贴到主屏幕上。相当于第n个圆形生成时,之前已经画过的n-1个半透明内圈,会被跟着重新贴一次到屏幕上,重叠区域的alpha值被反复叠加,最终越来越高 - 之前尝试把透明度逻辑移到
__init__没效果,是因为没有改动这块共享透明面的复用逻辑,累积绘制的本质问题没解决。
修正实现方案
要实现每个圆形的半透明效果独立、不交叉叠加,需要把“全局共享透明面累积绘制”改成“每个圆形独立管理专属图层”的模式:
- 不再使用跨圆形复用的全屏透明面,每个圆形在初始化时,单独生成一块和自身尺寸匹配的小透明面,只在这块小面上绘制自己的半透明内圈
- 按正确的层级合并图层:先画半透明内圈作为下层,再画圆形实体作为上层,两层合并成圆形自己的独立精灵图
- 不要在单个对象初始化时直接操作主屏幕刷新,所有对象创建完成后统一绘制,避免重复blit导致的画面重叠。
修正后的代码
首先是自定义圆形类的调整:
import pygame class circles(pygame.sprite.Sprite): def __init__(self, x, y, cs): pygame.sprite.Sprite.__init__(self) # 加载并缩放圆形实体图 self.base_circle = pygame.image.load("./assets/circle.png").convert_alpha() circle_size = ( int(round(2.25*(109 - 9*cs))), int(round(2.25*(109 - 9*cs))) ) self.base_circle = pygame.transform.scale(self.base_circle, circle_size) # 为当前圆形生成专属的半透明内圈图层,创建时启用alpha通道 self.halo_layer = pygame.Surface(circle_size, pygame.SRCALPHA) # 只在自身专属图层上绘制一次半透明内圈 pygame.draw.circle( self.halo_layer, (255, 255, 255, 10), # 内圈透明度可按需调整 (circle_size[0]//2, circle_size[1]//2), int(circle_size[0]/2) - 5 ) # 合并两层为圆形的最终显示图像:halo在下层,实体在上层 self.image = pygame.Surface(circle_size, pygame.SRCALPHA) self.image.blit(self.halo_layer, (0, 0)) self.image.blit(self.base_circle, (0, 0)) # 记录圆形的位置,以图形中心对齐传入的坐标 self.rect = self.image.get_rect(center=(x, y)) # 提供独立的绘制方法,方便主程序调用 def render(self, target_surface): target_surface.blit(self.image, self.rect)
然后是圆形放置函数的调整:
def Place_circles(screen, curve, circle_space, font): Circle_list = [] idx = [0, 0] for c in reversed(range(0, len(curve))): for p in reversed(range(0, len(curve[c]))): dist = math.sqrt( math.pow(curve[c][p][0] - curve[idx[0]][idx[1]][0], 2) + math.pow(curve[c][p][1] - curve[idx[0]][idx[1]][1], 2) ) if dist > circle_space: idx = [c, p] # 初始化圆形时不再传入共享透明面和主屏幕,所有图层由圆形自身管理 new_circle = circles( x=curve[c][p][0], y=curve[c][p][1], cs=4 ) Circle_list.append(new_circle) # 直接把当前圆形绘制到主屏幕,因为每个圆形的图像都是独立合并好的,不会累积污染 new_circle.render(screen) pygame.display.update()
注意事项
- 创建带透明效果的Surface时,必须传入
pygame.SRCALPHA标记,否则设置的alpha通道会被自动填充为黑色,无法实现半透明效果 - 独立图层模式下,多个圆形重叠时会按照正常的图形alpha混合规则计算最终透明度,不会出现无限制累积变实的问题
- 后续可以把半透明图层绘制的逻辑封装到类的内部方法里,按照规范调整方法名,减少跨对象的全局函数依赖
- 如果需要实现更流畅的批量绘制,可以把所有圆形实例加入
pygame.sprite.Group,直接调用Group的draw方法一次性渲染所有对象,减少重复刷新的性能消耗。
内容的提问来源于stack exchange,提问作者Gess1t
相关产品推荐
相关产品推荐

