处理pygame中图像背景透明的问题

1 投票
2 回答
4318 浏览
提问于 2025-04-16 19:57

我正在用pygame做一个简单的打字练习软件。我的问题是,我用了一张背景是白色的图片,叫做waves1.png。我已经设置了希望图片中的白色部分变成透明(self.image.set_colorkey((255, 255, 255))),这样做的效果是除了文本块以外,其他地方的白色都变透明了。但是,当波浪和text对象重叠时,波浪的白色背景还是会显示在文本上面。如果你有pygame,可以试着运行一下这个程序(除了waves1.png这张图片)。

import pygame
from pygame.locals import *

class TextSprite(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.wordList = ['words yes', 'hello', 'this is a sentence', 'this is another sentence'] # read directly from external file
        self.pos = 0
        self.wordNum = 0
        self.update1()

    def update1(self):
        # Render the given word
        self.image = pygame.font.Font(None, 36).render(self.wordList[self.wordNum], 1, (0, 0, 0))
        # Render the correctly guessed letters
        self.correct = pygame.font.Font(None, 36).render(self.wordList[self.wordNum][:self.pos], 1, (255, 0, 0))
        # Copy correct letters onto given word
        self.image.blit(self.correct, (0, 0))

        self.rect = self.image.get_rect()
        # set the center of the center the given word to the center of the screen
        self.rect.center = pygame.display.get_surface().get_rect().center

    def keyin(self, key):
        word = self.wordList[self.wordNum]
        letter = word[self.pos]
        if letter == key:
            self.pos = self.pos + 1
        if self.pos == len(word):
            self.reset()
        self.update1()

    def reset(self):
        self.pos = 0
        self.wordNum = self.wordNum + 1
        self.update1()



class Waves(pygame.sprite.Sprite):

    # Constructor. Pass in the color of the block, 
    # and its x and y position
    def __init__(self, filename):
        # Call the parent class (Sprite) constructor
        pygame.sprite.Sprite.__init__(self) 

        # Create an image of the block, and fill it with a color.
        # This could also be an image loaded from the disk.
        self.image = pygame.image.load(filename).convert()
        # makes any white in the image transparent
        self.image.set_colorkey((255, 255, 255))
        self.rect = self.image.get_rect()

    # Decrease the y coordinate so the waves look like they're moving up
    def update(self, text):
        self.rect.y = self.rect.y - 6
        if self.rect.y <= 200:
            text.reset()
            self.rect.y = 485


def main():

    #I - Import and initialize
    pygame.init()

    #D - Display configuration
    # The screen variable is a pygame Surface object
    # Note that the set_mode() method creates a Surface object for you automatically
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Typing Game")

    #E - Entities (just background for now)
    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((255, 255, 255))
    screen.blit(background, (0,0))



    #A - Action (broken into ALTER steps)

    #A - Assign values to key variables
    clock = pygame.time.Clock()
    keepGoing = True

    # Collect the sprite in a list
    all = pygame.sprite.RenderPlain()
    waveList = pygame.sprite.RenderPlain()

    text = TextSprite()
    all.add(text)

    waves = Waves("waves1.png")
    waveList.add(waves)
    waves.rect.x = 0
    waves.rect.y = 485

    #L - Set up main loop
    while keepGoing:

        #T - Timer to set frame rate
        # Tick is a method in the Clock class that determines the maximum frame rate
        clock.tick(30)

        #E - Event handling
        for event in pygame.event.get():
            if event.type == QUIT:
                keepGoing = False
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    keepGoing = False
                else:
                    text.keyin(event.unicode)

        # update position of waves
        waves.update(text)

        # clears screen
        all.clear(screen, background)

        # update screen
        all.draw(screen)

        waveList.clear(screen, background)
        waveList.draw(screen)


        # display.flip is a method that copies everything from the screen object to the actual visual display
        pygame.display.flip()

pygame.quit ()
if __name__ == '__main__': main()

2 个回答

1

做得好!你实际上已经正确地利用了透明度和颜色键(比如,确保对表面调用了转换,确保将颜色传递给set_colorkey方法等等)。

问题出在你对两个精灵组“all”和“waveList”的绘制和清除顺序上。在你通过调用all.draw渲染文本块之后,接着又调用了waveList.clear。

问题是:一旦你绘制了文本精灵,就不想清除波浪精灵下面的空间,否则会把已经绘制的文本块覆盖掉。

如果你想正确处理这个问题,可以尝试按以下顺序进行:

  1. waves.update()
  2. all.clear(screen, background)
  3. waveList.clear(screen, background)
  4. all.draw(screen)
  5. waveList.draw(screen)

更简单地说,只需把waveList.clear(screen, background)移动到all.clear(screen, background)下面一行就可以了;这样就可以解决问题。

当我处理精灵组时,通常会将它们分组,使每个精灵组按照这个顺序调用相同的方法:清除、更新、碰撞检查(如果有的话)、绘制。

这样通常能以正确的顺序处理事情。然后你可能还需要注意精灵的层次关系,但那是另一个话题,留到以后再说。

1

我不知道这对你来说是否可行,但使用png格式的原生透明度效果会更好。

如果你能自己编辑或重新制作png图片,试着使用透明背景。

这样的话,在加载图片后可以用convert_alpha()来处理它。(而不是使用颜色键)

http://pygame.org/docs/ref/surface.html#Surface.convert_alpha

编辑:还有一个方面是,图片可能有一个透明通道,这会影响颜色键。最好确保你不要同时使用这两者。

我听说你可以通过编程来检测图片的透明通道。类似于...

 if self.image.get_masks()[3]!=0:
   print "image has alpha!"

可以查看这里 http://pygame.org/docs/ref/surface.html#Surface.get_masks

希望这对你有帮助

撰写回答