I'm working on my first game, and I decided to add some trees that twinkle to the game. For some reason when the image is blitted to the screen using pygames blit
function it changes the color of the yellow parts of the tree where it twinkles.
Some googling led me to suspect the alpha channels could be the issue so I changed the load part of the image to use convert_alpha()
to take care of transparency, which then displays the bottom tree correctly under a black background, but I get strange shading on the rest of the images that are being loaded prior to the trees, and as you can see, the yellow parts now have black rings around them.
I initially used these two lines of code to load the images.
self.sheet = pygame.image.load(filename).convert()
screen.blit(tileImage, (centered_x, centered_y))
The actual image looks like this when I open it up in Paint3D
Here is the actual class I'm using to process my sprite sheets
class spritesheet(object):
def __init__(self, filename):
try:
self.sheet = pygame.image.load(filename).convert()
except pygame.error as message:
print('Unable to load spritesheet image:', filename)
raise SystemExit(message)
#Load a specific image from a specific rectangle
def image_at(self, rectangle, colorkey = None):
"Loads image from x,y,x+offset,y+offset"
rect = pygame.Rect(rectangle)
image = pygame.Surface(rect.size).convert()
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
# Load a whole bunch of images and return them as a list
def images_at(self, rects, colorkey = None):
"Loads multiple images, supply a list of coordinates"
return [self.image_at(rect, colorkey) for rect in rects]
# Load a whole strip of images
def load_strip(self, rect, image_count, colorkey = None):
"Loads a strip of images and returns 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)
def load_sheet(self,width,height,rows,image_count, colorkey = None):
'''Loads an entire sprite sheet, assumes sprites are equally spaced, returns them as list of lists '''
sprite_sheet = []
for i in range(rows):
sprite_sheet.append(self.load_strip((0, 0+i*height, width, height),image_count,colorkey))
print('len(sprite_sheet)',len(sprite_sheet))
return sprite_sheet
Any ideas what could cause this? It's not displaying the shadows of the tree either which I thought was odd, but that is a separate question I think.
Edit Here is a full example code. The images I'm using can be downloaded here
import pygame
import os
import errno
from pathlib import Path
TILEWIDTH = 143 #holds the tile width and height
TILEHEIGHT = 137
TILEHEIGHT_HALF = TILEHEIGHT /2
TILEWIDTH_HALF = TILEWIDTH /2
DEBUG = False
grass = [(0,0)]
class Enum(tuple):
'''Allows enumerations to be defined as
options = Enum(['COLOR','NOCOLOR'])
Acccessing enum: options.COLOR returns 0, options.NOCOLOR returns 1
'''
__getattr__ = tuple.index
options = Enum(['CONVERTALPHA','CONVERT'])
class spritesheet(object):
def __init__(self, filename,load_options=None):
try:
if (load_options == options.CONVERTALPHA):
self.sheet = pygame.image.load(filename).convert_alpha()
elif (load_options == options.CONVERT):
self.sheet = pygame.image.load(filename).convert()
elif (load_options == None):
self.sheet = pygame.image.load(filename).convert()
except pygame.error as message:
print('Unable to load spritesheet image:', filename)
raise SystemExit(message)
# Load a specific image from a specific rectangle
def image_at(self, rectangle, colorkey = None):
"Loads image from x,y,x+offset,y+offset"
rect = pygame.Rect(rectangle)
image = pygame.Surface(rect.size).convert()
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
# Load a whole bunch of images and return them as a list
def images_at(self, rects, colorkey = None):
"Loads multiple images, supply a list of coordinates"
return [self.image_at(rect, colorkey) for rect in rects]
# Load a whole strip of images
def load_strip(self, rect, image_count, colorkey = None):
"Loads a strip of images and returns 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)
def load_sheet(self,width,height,rows,image_count, colorkey = None):
'''Loads an entire sprite sheet, assumes sprites are equally spaced, returns them as list of lists '''
sprite_sheet = []
for i in range(rows):
sprite_sheet.append(self.load_strip((0, 0+i*height, width, height),image_count,colorkey))
print('len(sprite_sheet)',len(sprite_sheet))
return sprite_sheet
map = [[0, 0, 0,],
[0, 30, 0]]
def get_file_path(file_directory):
'''
get_file_path
Description: Get absolute file path from current working directory, it will
raise a FileNotFoundError exception if the file doesn't exist
Inputs: file_directory- filepath from running directory
Outputs: Absolute path to file
run_directory - Is the current run path, while file_directory is the file path
below the current root directory.
'''
run_directory = os.path.dirname(__file__)
file_path = Path(run_directory + file_directory)
if (not os.path.isfile(file_path) ):
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),file_path)
return str(file_path)
class Map():
def __init__(self,world):
self.map = world
ss = spritesheet(get_file_path('/Assets/Textures/Grass-Spritesheet_Blocks.png'))
tree_sheet = spritesheet(get_file_path('/Assets/Textures/trees_twinkle.png'),options.CONVERTALPHA)
self.grass = ss.load_sheet(192,192,5,6,(0,0,0))
self.trees = tree_sheet.load_sheet(192,192,1,4)
self.init = True
self.rect = []
self.previous_update = False
def render(self,screen,updatemap):
#Only update map when player screen changes
self.reset = False
self.foreground = []
if (True):#(self.init == True) or True):
tree_width = 100
tree_height = 100
actual_tree_width = 175
actual_tree_height = 182
for row in range(0,len(self.map)):
for col in range(0, len(self.map[row])):
cart_x = col * TILEWIDTH_HALF
cart_y = row * TILEHEIGHT_HALF
iso_x = (cart_x - cart_y)
iso_y = (cart_x + cart_y)/2
centered_x = screen.get_rect().centerx + iso_x
centered_y = screen.get_rect().centery/2 + iso_y
if (self.map[row][col] < 30):
tileImage = self.grass[grass[self.map[row][col]][0]][grass[self.map[row][col]][1]]
else:
tileImage = self.grass[grass[0][0]][grass[0][1]]
tileForeground = self.trees[0][0]
self.foreground.append({'image': self.trees[0][0], 'coordinates': (centered_x,centered_x)})
if (self.map[row][col] < 30):
screen.blit(tileImage, (centered_x, centered_y))
else:
screen.blit(tileImage, (centered_x, centered_y))
screen.blit(tileForeground,(centered_x, centered_y-96),special_flags=pygame.BLEND_PREMULTIPLIED)
self.init = False
if __name__ == '__main__':
import pygame
from pygame.locals import ( K_w, K_s, K_a, K_d, K_UP, K_DOWN, K_ESCAPE, KEYDOWN, QUIT, )
running = True
pygame.init()
screen = pygame.display.set_mode((800, 600),pygame.DOUBLEBUF)
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
worldMap = Map(map)
worldMap.render(screen,False)
pygame.display.update()
There's a colorkey option for the load_sheet which is set to black. I've tried removing that
BLEND_PREMULTIPLIED
flag described here? \$\endgroup\$