开发者

When blitting a sprite with colorkey transparency in Pygame, the areas that should be transparent are black

I have a simple program that blits a background image onto the display area, and then puts a sprite in the upper left corner. The sprite uses colorkey transparency, but when I blit the sprite, the areas that should be transparent are black, instead. The color u开发者_运维问答sed for the colorkey is a greenish color, so the colorkey is doing something, at least.

Here's my main module, which has most of the display code:

import pygame
from pygame.locals import *

import sprites

try: 
    import psyco
    psyco.full()
except:
    print "psyco failed to import"

class PatchCon(object): # main game class
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((832, 768))
        pygame.display.set_caption('PatchCon')
        self.setBackground()
        self.sprites = pygame.sprite.RenderPlain((sprites.Actor("Reimu")))

    def refresh(self):
        self.sprites.update()
        self.screen.blit(self.background, (0,0))
        self.sprites.draw(self.screen)
        pygame.display.update()

    def setBackground(self):
        self.background = pygame.Surface(self.screen.get_size())
        backgrounds = sprites.loadImage('patchconbackgrounds.png')
        self.background.blit(backgrounds[0], (0,0), sprites.bgcoords[3])
        self.background = self.background.convert()

    def mainLoop(self):
        while True:
            for event in pygame.event.get():
                if event.type == QUIT: return
            self.refresh()



if __name__ == '__main__':
    game = PatchCon()
    game.mainLoop()

My other module, 'sprites.py', deals with loading images and constructing sprite objects:

import os
import pygame
from pygame.locals import *


# there are four different backgrounds compiled into one big image.
bgcoords = ((0,0,832,768),
            (0,769,832,1537),
            (833,0,1664,768),
            (833,769,1664,1537))

# the top left pixels of characters' sprites in the sprite sheet
charCoords = { "Reimu" : (1, 10), 
               "Marisa" : (334, 10)} 


def loadImage(name, colorkey=None):
    fullname = os.path.join('images', name)
    try:
        image = pygame.image.load(fullname)
    except pygame.error, message:
        print 'Cannot load image:', fullname
        raise SystemExit, message
    image = image.convert_alpha()
    if colorkey is not None:
        if colorkey is -1:
            colorkey = image.get_at((0,0))
        image.set_colorkey(colorkey, RLEACCEL)
    return image, image.get_rect()



class SpriteDict(object):
    """SpriteDict has a list that holds all of the different images
    the sprites have.  It's in charge of finding character's sprites on a big
    sprite sheet and loading them into that list."""
    def __init__(self):
        self.spritesheet = loadImage("patchconsprites.png", pygame.Color('#007888'))[0]
        # more code here

    def choose(self, dir, step, color):
        """This function returns an image from the sprite list."""



class Actor(pygame.sprite.Sprite):
    def __init__(self, name):
        pygame.sprite.Sprite.__init__(self)
        self.sprites = SpriteDict(name)
        self.image = self.sprites.choose('N', 0, 1)
        self.rect = self.image.get_rect()

All the tutorials I've found lead me to believe this code is correct. Anyone see what I'm doing wrong?


EDIT: searching around the related questions, I found that image.convert() doesn't preserve alpha pixels, so as suggested in another question, I changed it to image.convert_alpha(). Now, however, instead of black areas, I just get the color of the colorkey. I've triple-checked the color value, and I'm certain its correct. Anything else I could be missing?


Well, I solved my problem. It involved changing the guts of SpriteDict so that it didn't call loadImage(), but instead handled loading the image and applying the transparency in the constructor itself, like so:

class SpriteDict(object):
    def __init__(self, name):
        self.spriteSize = (35, 35)
        # change this old line:
        #self.spritesheet = loadImage("patchconsprites.png", (0,120,136,0))[0]
        # to this:
        self.spritesheet = pygame.image.load("./images/patchconsprites.png")
        self.sprites = []
        start = charCoords[name]
        char = list(start)
        image = pygame.Surface((35,35))
        for y in range(5):
            char[0] = start[0]
            for x in range(9):
                rect = (char[0], char[1], char[0]+self.spriteSize[0], char[1]+self.spriteSize[1])
                image.blit(self.spritesheet, (0,0), rect)
                # and put the transparency code here:
                image = image.convert()
                colorkey = image.get_at((0,0))
                image.set_colorkey(colorkey, RLEACCEL)
                # end new code
                self.sprites.append(image)
                char[0] += self.spriteSize[0]+2
            char[1] += self.spriteSize[1]+2

It makes my code a lot messier, and probably a good bit slower, but it works. If someone could tell me why it has to work this way, and possibly how to make it not so ugly, that'd be great.


The problem with the code that is posted in the question now is probably that you are mixing convert_alpha(), which results in a sprite with an alpha channel, with a color with alpha 255. A colorkey check in SDL is just an integer comparison, so even though you did not specify an alpha channel, you are only going to match 0x007888FF, not e.g. 0x007888FE or 0x00788800.

If you have a sprite with an alpha channel in the first place, there is no reason to use a colorkey. Simply use the sprite's alpha channel to make the parts you want transparent.


Your call to loadImage passes (implicitly) None as the colorkey parameter. Your loadImage function, when the colorkey is None, does not set any colorkey.


Here's a block of image loading code I found within the pygame examples. You can plug this into your main python file to handle the same job as your script, without having to import it:

import os, sys #required to use this block
...
-snip-
...
def load_image(name, colorkey=None):
    fullname = os.path.join('name for images folder here', name)
    try:
        image = pygame.image.load(fullname)
    except pygame.error, message:
        print 'Cannot load image:', name
        raise SystemExit, message
    image = image.convert()
    if colorkey is not None:
        if colorkey is -1:
            colorkey = image.get_at((0,0))
        image.set_colorkey(colorkey, RLEACCEL)
    return image, image.get_rect()

Some notes:

Loading a circular object whose diameter touches the rect's dimensions will cause issues. You can fix that by making the image size larger than the circle. This happened when loading PNGs, but I don't know what happens when loading other extension formats.

This block is extremely versatile; you can copy-paste this in any script and it will work. When you want to load an image, call load_image('image.bmp', -1). a colorkey of -1 will tell the function to locate the pixel at 0,0 of the sprite, then use that as the colorkey.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜