使图像背景透明的问题:pygame

发布于 2024-11-16 11:57:20 字数 4293 浏览 1 评论 0原文

我正在使用 pygame 编写一个简单的打字导师。我的问题是我使用的是白色背景的图像 waves1.png。现在我已经指定我希望白色在图像中是透明的 (self.image.set_colorkey((255, 255, 255))),并且它适用于除文本块之外的所有内容。当波浪与文本对象相交时,波浪的白色背景显示在文本顶部。如果你有 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()

I'm in the middle of working on a simple typing tutor using pygame. My problem is that I'm using an image that has a white background, waves1.png. Now's I've specified that I want white to be transparent in the image (self.image.set_colorkey((255, 255, 255))) and it is for everything except the text block. When the waves intersect with the text object, the white background of the waves show on top of the text. You can try running this if you have pygame (with the exception of the waves1.png image).

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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

携君以终年 2024-11-23 11:57:20

我不知道这是否适合您,但您应该使用 png 的原生 alpha 透明度获得更好的结果。

如果您可以自己编辑/重新创建 png,请尝试使用透明背景。

从那里,您可以在加载图像后使用convert_alpha()。 (而不是使用颜色键)

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

编辑: 另一方面,图像可能具有干扰色键的 Alpha 通道。最好确保您不会尝试同时使用两者。

我听说您可以通过编程方式检测图像的 Alpha 通道。类似...

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

请参见此处 http://pygame.org/docs/ref/ surface.html#Surface.get_masks

HTH

I don't know if it's an option for you, but you should get better results with png's native alpha transparency.

If you can edit/recreate the png yourself, then try using a transparent background.

From there, you can use convert_alpha() arfter loading the image. (instead of using a colorkey)

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

EDIT: one other aspect, is that the image may have an alpha channel interfering with the colorkey. Best to ensure you're not trying to use both.

I'm told that you can detect an image's alpha channel programmatically. Something like ...

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

See here http://pygame.org/docs/ref/surface.html#Surface.get_masks

HTH

如梦初醒的夏天 2024-11-23 11:57:20

干得好!实际上,您已经正确地完成了所有操作,以利用透明度和颜色键(即,确保在表面上调用转换,确保将颜色传递到 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)

(more简而言之,只需将 waveList.clear(screen, background) 移至 all.clear(screen, background); 下方的行即可)

当我使用精灵组时,我通常尝试将其分组,以便每个精灵group 按以下顺序调用相同的方法:清除、更新、碰撞检查(如果有)、绘制。

这通常会按正确的顺序处理事情。那么你可能仍然需要注意精灵是否有分层,但那是另一回事了。

Well done! You've actually done everything correctly to take advantage of transparency and colorkey (ie, making sure to call convert on the surface, making sure to pass the color into the set_colorkey method, etc).

The problem is with the order of calls to draw and clear on your respective sprite groups, "all" and "waveList". After you've rendered the text blocks by calling all.draw, you then follow it with the call to waveList.clear.

Here's the problem: once you've drawn the text sprites, you don't want to clear the space underneath the wave sprites, or that will wipe out the area that overlaps the already-drawn text blocks.

If you want to do this properly, try doing it in this order:

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

(more simply, just move waveList.clear(screen, background) to the line just below all.clear(screen, background); that should do it)

When I'm working with sprite groups, I usually try to group it so that each sprite group calls the same method in this order: clears, updates, collision checks (if any), draws.

This usually handles things in the right order. Then you still may have to pay attention to whether there is any layering of sprites, but that's another story for another day.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文