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:
Related
So i started doing a game to pass the time and i don't know how to solve this issue:
my player is a part of a sprite sheet, the sprite sheet has a alpha layer so its transparent but when i divide my sprite sheet into small sprite, this alpha layer disapear and i have a black bg instead... I tried using set_colorkey([0, 0, 0]) to remove the black bg, but beceause my player is dark skinned, my player partially disappear. Any suggestion?
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("assets/img/plr.png")
self.image = self.get_image(0, 0)
self.image = pygame.transform.scale(self.image, (96, 96))
self.image.set_colorkey([0, 0, 0])
self.rect = self.image.get_rect(center=(250, 250))
def get_image(self, x, y):
image = pygame.Surface([48, 48])
image.blit(self.image, (0, 0), (x, y, 48, 48))
return image
def update(self):
pass
The alpha channel "disappears" because you create a Surface without alpha channel (RGB). You have to use the SRCALPHA flag to create a Surface with an alpha channel (RGBA). Also see pygame.Surface:
image = pygame.Surface([48, 48])
image = pygame.Surface([48, 48], pygame.SRCALPHA)
For a surface without alpha channels you can set the transparent color key with set_colorkey:
image = pygame.Surface([48, 48])
image.set_colorkey((0, 0, 0))
import pygame as pygame , sys
pygame.init()
size = (700,500)
window_game = pygame.display.set_mode(size)
print(pygame.mouse.get_cursor())
_run_ = True
class mySprite(pygame.sprite.Sprite):
def __init__ (self,width,height,cord_x,cord_y,color):
super().__init__()
self.image = pygame.Surface([width,height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.center = [cord_x,cord_y]
#my background image
bgimg = pygame.image.load("download.jpg")
bgimg = pygame.transform.smoothscale(bgimg, size)
placeSP = [mySprite(50,20,100,150,(10,205,120))]
placeSP_group = pygame.sprite.Group()
Clock = pygame.time.Clock()
FPS = 240
while _run_:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.QUIT
sys.exit()
placeSP.append(mySprite(50,20,100,170,(10,25,0)))
pygame.display.flip()
window_game.blit(bgimg,(0,0))
placeSP_group.draw(window_game)
placeSP_group.add(placeSP[:])
Clock.tick(FPS)
now the problem I have is that the download.jpg is 4k res and if I try to fit that image in my window the img is very blurry and i have also tried many more img but they were all blurry
do I try to get a picture of the window size or do i have to do something else pls tell me....
You can't. You can choose between a blurred image with pygame.transform.smoothscale
bgimg = pygame.transform.smoothscale(bgimg, size)
or a jagged image with pygame.transform.scale
bgimg = pygame.transform.scale(bgimg, size)
If you don't want that, you have to use a higher resolution image.
I'm trying to make a game using pygame but I came up with an issue of people getting annoyed of the resolution they're working with, and can't resize window without stretching it.
Here is an example picture of what I'm trying to achieve.
here's what I tried.
window.blit(pg.transform.scale(screen, (window.get_size())), (0, 0)) # The game screen stretching
PS: It's hard to explain so I had to show an image
Use the following algortihm:
Get the bounding rectangle of the image and set the center of the rectangle to the center of the destination rectangle.
Use pygame.Rect.fit() to resize and move the aspect ratio rectangle into the destination rectangle.
Use the size of the new rectangle to scale the image.
blit the image at the position of the rectangle.
def blit_fit(dest_surf, image, dest_rect):
image_rect = image.get_rect(center = dest_rect.center)
fit_rect = image_rect.fit(dest_rect)
scaled_image = pygame.transform.scale(image, fit_rect.size)
dest_surf.blit(scaled_image, fit_rect)
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
image = font.render("Screen", True, (255, 255, 0))
pygame.draw.rect(image, (255, 255, 255), image.get_rect(), 1)
def blit_fit(dest_surf, image, dest_rect):
image_rect = image.get_rect(center = dest_rect.center)
fit_rect = image_rect.fit(dest_rect)
scaled_image = pygame.transform.scale(image, fit_rect.size)
dest_surf.blit(scaled_image, fit_rect)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
blit_fit(window, image, window.get_rect())
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()
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
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)