How to remove black background from sprite in sprite sheet in pygame - python

I am trying to remove the black background from this sprite taken from a sprite sheet. As you'll see in the following is the code and suggested by this post (Transparent spritesheet has black background), I've tried to use the pygame.SRCALPHA flag but this does not seem to work. I've even tried to use the same sprite sheet after converting it with a transparent background and I get the same black border around the Mario sprite.
import pygame
pygame.init()
def imgcolorkey(image, colorkey):
if colorkey is not None:
if colorkey is -1:
colorkey = image.get_at((0, 0))
image.set_colorkey(colorkey, RLEACCEL)
return image
class SpriteSheet(object):
sprite_sheet = None
def __init__(self, file_name):
#self.sprite_sheet = pygame.image.load(file_name).convert()
self.sprite_sheet = pygame.image.load(file_name,pygame.SRCALPHA)
#self.sheet = load_image(filename)
def imgat(self, rect, colorkey = None):
rect = pygame.Rect(rect)
#image = pygame.Surface(rect.size).convert()
#image = pygame.Surface(rect.size, pygame.SRCALPHA ).convert()
image = pygame.Surface(rect.size, pygame.SRCALPHA)
#image.fill(transColor)
image.blit(self.sprite_sheet, (0, 0), rect)
return imgcolorkey(image, colorkey)
def imgsat(self, rects, colorkey = None):
imgs = []
for rect in rects:
imgs.append(self.imgat(rect, colorkey))
return imgs

Found the problem.
I was not sending the colorkey argument of -1 when building the image in my main class.
I changed:
mario_image = SPRITE_SHEET.imgsat(MARIO_COORD)
to:
images = SPRITE_SHEET.imgsat(MARIO_COORD,-1)

Related

Is it possible to display a change of color to a pygame Sprite?

I have a pygame Sprite which is generated through Font. It's just a 16x16 surface with a letter printed on it and blitted.
The sprite has a timer (it's a powerup) and when it's near the end of it's life I want it to flash a random color on every update. I've been successful doing this with other text but that text isn't a sprite, just a string I blit to the score board. I figured this would be the same, but once the sprite is generated, no matter how much I change the sprite's color, the change doesn't translate to the screen (though if I print(self.color) I can see the updated color tuple in the console).
I've tried putting the random color picker inside the Class as well as trying outside the class in my while loop. I can change the color easily enough, but the sprite on screen doesn't actually change. I am not using an external sprite image, just a Font blitted to a pygame.Surface.
This is my item class.
class Item(pygame.sprite.Sprite):
def __init__(self, name, pos):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.image = pygame.Surface([16, 16])
self.image.set_colorkey(black)
self.font = pygame.font.Font("./fonts/myfont.ttf", 16)
self.pos = pos
if self.name == "health":
self.color = (255, 0, 0)
self.text = self.font.render("H", True, self.color)
self.lifespan = 200
self.lifespan_counter = 0
self.image.blit(self.text, (0, 0))
def update(self):
# Update timer
self.lifespan_counter += 0.1
if self.lifespan_counter >= self.lifespan:
self.kill()
# Update position
self.rect.center = (int(self.pos[0]), int(self.pos[1]))
And then at the bottom of my def main() in the while loop, I have this stuff:
random_color_counter += 1
if random_color_counter > 3:
random_color = get_random_color()
random_color_counter = 0
screen.fill(background)
text_box.fill(blue)
game_box.fill(white)
# Update the sprites positions and then draw them to game_box surface
player_sprites.update()
player_bullet_sprites.update()
enemy_sprites.update()
enemy_bullet_sprites.update()
item_sprites.update()
player_sprites.draw(game_box)
player_bullet_sprites.draw(game_box)
enemy_sprites.draw(game_box)
enemy_bullet_sprites.draw(game_box)
item_sprites.draw(game_box)
...
for i in item_sprites:
game_box.blit(i.image, (int(i.pos[0]), int(i.pos[1])))
# Refresh everything
pygame.display.update()
And this is the function that picks a new color.
def get_random_color():
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
return r, g, b
And then I can use the color random_color for most things, just not sprites apparently.
Like I said, this displays the sprite just fine at the position it is supposed to (where the baddie died), but I cannot seem to have a change to the item sprites color translate to the screen. I'm just not seeing what I'm doing wrong.
When you want to change the color of the text, you have to render the text again and update the text Surface. Write a change_color method:
class Item(pygame.sprite.Sprite):
# [...]
def change_color(self, color):
self.image = pygame.Surface([16, 16])
self.image.set_colorkey(black)
self.color = color
self.text = self.font.render("H", True, self.color)
self.image.blit(self.text, (0, 0))

Pygame image transparency confusion

I have read the top 20 posts relating to this issue here, read many examples on Google, tried using .convert(), .convert_alpha(), tried with neither, tried with .png, .gif, tried with the top 5 different images on google. Please someone help me figure out how to make the pieces show with a transparent background.
Here is ALL the code:
import pygame
pygame.init()
print("1")
screen_size = (600, 600)
blue = (100, 225, 225)
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("Chess")
class SpriteSheet:
def __init__(self, filename):
"""Load the sheet."""
try:
self.sheet = pygame.image.load(filename).convert.alpha()
except pygame.error as e:
print(f"Unable to load spritesheet image: {filename}")
raise SystemExit(e)
def image_at(self, rectangle, colorkey = None):
"""Load a specific image from a specific rectangle."""
# Loads image from x, y, x+offset, y+offset.
rect = pygame.Rect(rectangle)
image = pygame.Surface(rect.size)
image.blit(self.sheet, (0, 0), rect)
if colorkey is not None:
if colorkey == -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey, pygame.RLEACCEL)
return image
def images_at(self, rects, colorkey = None):
"""Load a whole bunch of images and return them as a list."""
return [self.image_at(rect, colorkey) for rect in rects]
def load_strip(self, rect, image_count, colorkey = None):
"""Load a whole strip of images, and return them as a list."""
tups = [(rect[0]+rect[2]*x, rect[1], rect[2], rect[3])
for x in range(image_count)]
return self.images_at(tups, colorkey)
print("2")
class Game:
def __init__(self):
self.playing = False
self.move = 0
self.player_turn = 1
self.quit = False
def quit(self):
self.quit = True
print("3")
class Piece:
def __init__(self):
self.sprite = None
self.spacial = [0, 0, 0]
self.temporal = [0, 0]
self.position = [self.spacial, self.temporal]
self.color = ""
self.type = ""
print("4")
chess_image = SpriteSheet('ChessPiecesArray.png')
colors = ["White", "Black"]
types = ["K", "Q", "B", "N", "R", "P"]
rect_piece = (0, 0, 133, 133)
print("5")
class ChessSet:
def __init__(self):
self.set = []
def create_set(self):
for i in range(2):
for j in range(6):
this_piece = Piece()
this_piece.color = colors[i]
this_piece.type = types[j]
rect_set = (133*j, 133*i, 133*(j+1), 133*(i+1))
this_piece.sprite = SpriteSheet.image_at(chess_image, rect_set)
self.set.append(this_piece)
print("6")
chess = Game()
set_one = ChessSet()
set_one.create_set()
print("7")
while not chess.quit:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
chess.quit()
screen.fill(blue)
screen.blit(set_one.set[0].sprite, (10, 10))
pygame.display.flip()
Here are a few images I spent time trying:
EDIT: Here is the screenshot of my error message over my code with the suggested change
You have to use an image with a transparent background. Be careful and do not download a preview of an image with a checkered background. Just because they are PNGs doesn't mean the background is transparent. The background can still be opaque. Use the following image
If you copy a transparent Surface to another Surface the target Surface has to provide transparency respectively per pixel alpha.
You can enable additional functions when creating a new surface. Set the SRCALPHA flag to create a surface with an image format that includes a per-pixel alpha. The initial value of the pixels is (0, 0, 0, 0):
my_surface = pygame.Surface((width, height), pygame.SRCALPHA)
and adapt the method image_at of the class SpriteSheet. Use pygame.SRCALPHA:
class SpriteSheet:
# [...]
def image_at(self, rectangle, colorkey = None):
"""Load a specific image from a specific rectangle."""
# Loads image from x, y, x+offset, y+offset.
rect = pygame.Rect(rectangle)
image = pygame.Surface(rect.size, pygame.SRCALPHA) # <----
image.blit(self.sheet, (0, 0), rect)
if colorkey is not None:
if colorkey == -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey, pygame.RLEACCEL)
return image
Or use convert_alpha():
class SpriteSheet:
# [...]
def image_at(self, rectangle, colorkey = None):
"""Load a specific image from a specific rectangle."""
# Loads image from x, y, x+offset, y+offset.
rect = pygame.Rect(rectangle)
image = pygame.Surface(rect.size).convert_alpha() # <----
image.fill((0, 0, 0, 0)) # <---
image.blit(self.sheet, (0, 0), rect)
if colorkey is not None:
if colorkey == -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey, pygame.RLEACCEL)
return image
See also:
How can I make an Image with a transparent Backround in Pygame?
How do I blit a PNG with some transparency onto a surface in Pygame?
Note that chess pieces can also be drawn through a Unicode text.
See Displaying unicode symbols using pygame

Is it possible to change sprite colours in Pygame?

I'm making a game in Python using Pygame that includes a small avatar maker before the game starts, but instead of creating a big sprite sheet with 88 different combinations of hairstyles and colours, is there a way that I can just use a generic .png image of each hairstyle and apply colour to it in-game?
The hairstyles are saved as .png images with alpha and anti-aliasing, so they are not just one shade of colour. I've got 8 different hairstyles and 11 different colours. It wouldn't be a problem to load them in as a sprite sheet and clip them in-game, but if there was a way to apply colour (or hue) in the game then not only would it be easier on the memory, but would open it up to more possibilities.
If the image is a "mask" image, with a transparent background and a white (255, 255, 255) mask, then you can "tint" the image with ease.
Load the image:
image = pygame.image.load(imageName)
Generate a uniform colored image with an alpha channel and the same size:
colorImage = pygame.Surface(image.get_size()).convert_alpha()
colorImage.fill(color)
Blend the image with maskImage, by using the filter BLEND_RGBA_MULT:
image.blit(colorImage, (0,0), special_flags = pygame.BLEND_RGBA_MULT)
A sprite class may look like this:
class MySprite(pygame.sprite.Sprite):
def __init__(self, imageName, color):
super().__init__()
self.image = pygame.image.load(imageName)
self.rect = self.image.get_rect()
colorImage = pygame.Surface(self.image.get_size()).convert_alpha()
colorImage.fill(color)
self.image.blit(colorImage, (0,0), special_flags = pygame.BLEND_RGBA_MULT)
Minimal example: repl.it/#Rabbid76/PyGame-ChangeColorOfSurfaceArea-4
import pygame
def changColor(image, color):
colouredImage = pygame.Surface(image.get_size())
colouredImage.fill(color)
finalImage = image.copy()
finalImage.blit(colouredImage, (0, 0), special_flags = pygame.BLEND_MULT)
return finalImage
pygame.init()
window = pygame.display.set_mode((300, 160))
image = pygame.image.load('CarWhiteDragon256.png').convert_alpha()
hue = 0
clock = pygame.time.Clock()
nextColorTime = 0
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
color = pygame.Color(0)
color.hsla = (hue, 100, 50, 100)
hue = hue + 1 if hue < 360 else 0
color_image = changColor(image, color)
window.fill((96, 96, 64))
window.blit(color_image, color_image.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Sprite:

Transparent spritesheet has black background

I'm working on a game using Python and Pygame. I created a sprite sheet for one of the enemies and got my code for it working. The problem is that the image appears to have a black background even though it is a transparent image. The code for it is this:
enemySheet = pygame.image.load("resources/Alien.png").convert_alpha()
transColor = (255,255,255)
cells = []
for n in range(3):
width, height=(36,32)
rect = pygame.Rect(n * width, 0, width, height)
image = pygame.Surface(rect.size).convert_alpha()
image.blit(enemySheet, (0,0), rect)
cells.append(image)
enemyImg = cells[0]
enemyImg.set_colorkey(transColor)
enemy = enemyImg.get_rect()
enemy.center = (216,216)
I have already tried a few things but nothing has worked. Any ideas are welcome.
New surfaces are filled with black by default. If you want to make it transparent you can either add a fourth number to the transColor (the alpha value) and then fill the image,
transColor = (255,255,255,0)
# In the for loop.
image = pygame.Surface(rect.size).convert_alpha()
image.fill(transColor)
or just pass the pygame.SRCALPHA flag:
image = pygame.Surface(rect.size, pygame.SRCALPHA)
A nicer solution would be to use pygame.Surface.subsurface to cut the sheet:
for n in range(3):
width, height = (36, 32)
rect = pygame.Rect(n * width, 0, width, height)
image = enemySheet.subsurface(rect)
cells.append(image)

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 used 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.

Categories

Resources