So I copied some code from the internet (http://programarcadegames.com/python_examples/f.php?file=platform_moving.py) just to experiment with pygame...
I've tried replacing the self.image.fill(BLUE) with self.rect = pygame.image.load("TheArrow.png")
Here's a little snippet of my code..
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
width = 40
height = 60
self.image = pygame.Surface([width, height])
self.image.fill(BLUE)
self.rect = pygame.image.load("TheArrow.png")
# Set a referance to the image rect.
self.rect = self.image.get_rect()
# Set speed vector of player
self.change_x = 0
self.change_y = 0
# List of sprites we can bump against
self.level = None
Here's the original code...
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
width = 40
height = 60
self.image = pygame.Surface([width, height])
self.image.fill(RED)
# Set a referance to the image rect.
self.rect = self.image.get_rect()
# Set speed vector of player
self.change_x = 0
self.change_y = 0
# List of sprites we can bump against
self.level = None
I want the image TheArrow.png to show up instead of a rectangle....
Rect object are not meant to store images. pygame.image.load() returns a Surface with the image. It can be used directly or blitted on another Surface.
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
width = 40
height = 60
self.image = pygame.image.load("TheArrow.png") #use the image Surface directly
self.rect = self.image.get_rect()
#the rest as in the original code
or:
def __init__(self):
""" Constructor function """
# Call the parent's constructor
super().__init__()
width = 40
height = 60
myimage = pygame.image.load("TheArrow.png")
self.image = pygame.Surface([width, height])
self.image.blit(myimage) #blit the image on an existing surface
self.rect = self.image.get_rect()
#the rest as in the original code
In the former case, the size of the Surface (its associated rect, which you can get with self.image.get_rect() is the same of the loaded image file.
In the latter, you set the size with [with, height]. If these does not correspond to the image size, the image will be cut (if bigger).
By the way, blitting a Surface on another Surface is what you do display the Surface on the screen. In pygame the screen is just another Surface, a bit special.
Have a look at the intro tutorial for more info.
Related
Ok so I have this code which in def draw, in if self.part == 1: I blit the main image in the middle, but this doesn´t center the image as I want, it just makes the spawning point in the middle, and the image starts from there, so the image always appears on the bottom right side. I want it to blit it in the middle, like the whole thing:
class GameScene(Scene):
def __init__(self, game, images, main_image, next_scene):
super().__init__(next_scene)
self.game = game
self.main_image = main_image
self.game_images = images
# Fade effect set-up
self.fade = False
self.fade_time = 0
self.current_alpha = 255
self.part = 1
self.record_text = font.render('Atiende',True, PURPLE)
self.record_text_rect = self.record_text.get_rect(center=(SCREEN_WIDTH/2, 70))
self.correct_image_rect = None
# Trying to use colliderect so it doesnt overlap
# this is the same code as before but adapted to use the gameimage class and the rects stored there
self.rects = []
# this is the fade stuff from before that was in draw. It really belongs here tbh
def update(self, dt):
if len(self.rects) < len(self.game_images):
i = len(self.rects)
x = random.randint(100,950)
y = random.randint(100,600)
self.game_images[i].rect.x = x
self.game_images[i].rect.y = y
margin = 5
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or self.game_images[i].rect.collidelist(rl) < 0:
self.rects.append(self.game_images[i].rect)
if self.part == 1 and self.fade:
self.fade_time += dt
if self.fade_time > fade_timer:
self.fade_time = 0
self.main_image.set_alpha(self.current_alpha)
self.record_text.set_alpha(self.current_alpha)
# Speed whichin the image dissapears
self.current_alpha -= 5
if self.current_alpha <= 0:
self.fade = False
self.part = 2
else:
# we reset the main image alpha otherwise it will be invisible on the next screen (yeah, this one caught me out lol!)
self.main_image.set_alpha(255)
# draw is similar to before, but a bit more streamlined as the fade stuff is not in update
def draw(self, screen):
super().draw(screen)
if self.part == 1:
screen.blit(self.record_text, self.record_text_rect)
# x = self.main_image.rect.x.center
# y = self.main_image.rect.y.center
screen.blit(self.main_image.image, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
else:
# Second half
text2 = font.render('¿Qué has visto?',True, PURPLE)
screen.blit(text2, (400,5))
# Show all similar images
cont = 0
for game_image in self.game_images:
game_image.draw(screen)
cont += 1
# We associate the correct rect to the correct image, to pass it later to the CORRECT Screen
self.correct_image_rect = self.game_images[self.game_images.index(self.main_image)].rect
The thing is, that the main_image that it comes through a parameter, its a proper class:
class GameImage(object):
def __init__(self, image):
self.image = image
self.rect = self.image.get_rect()
# this class has the set_alpha so the alpha can be passed on to its image
def set_alpha(self, alpha):
self.image.set_alpha(alpha)
# we pass the draw method the surface we want the image drawn on
def draw(self, surf):
surf.blit(self.image, self.rect)
So, as you can see, the GameImage class it´s an object that gets its rect as well, but I´m struggling to center that main image rect and blit into the screen the way I want. So yeah, I know how to do it with texts, as you can see on the self.recrod_text_rect, but I don´t know how to do it with this object and pass it to the screen.blit of the draw def.
You need to set the center of the image by the center of target Surface:
class GameImage(object):
def __init__(self, image):
self.image = image
self.rect = self.image.get_rect()
# this class has the set_alpha so the alpha can be passed on to its image
def set_alpha(self, alpha):
self.image.set_alpha(alpha)
# we pass the draw method the surface we want the image drawn on
def draw(self, surf):
self.rect.center = surf.get_rect().center # <---
surf.blit(self.image, self.rect)
See also How to Center Text in Pygame
I can't figure out why the code below renders the sprite without transparency (with the rest of the sprite filled with black). I'm almost a beginner to programming so it might be very obvious but no other answers to similar questions here helped me. The file has transparency and I used convert_alpha() when loading the file.
import pygame
class Piece(pygame.sprite.Sprite):
def __init__(self, kind, posx, posy, sheet, color):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image for the piece and fill it with the image
# TO DO: cut the right type of piece from a sheet, preferably before constructing the object
self.image = pygame.Surface([128, 128])
self.image.blit(sheet, [0, 0])
self.kind = kind # piece type, designated with an uppercase letter, eg. Q for queen
self.color = color # W or B for white or black
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
self.rect.x = posx
self.rect.y = posy
class App:
def __init__(self):
self._running = True
self._display_surf = None
self.size = self.weight, self.height = 1024, 768
self.sprites = pygame.sprite.Group() # all sprites in the app to iterate on
self.spritesheet = 0 # this will be loaded later
self.bgcolor = [200, 200, 200]
def on_init(self):
pygame.init()
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._running = True
self.spritesheet = pygame.image.load("resources/w_queen_png_shadow_128px.png", "r").convert_alpha()
self.sprites.add(Piece("Q", 64, 64, self.spritesheet, "W"))
def on_event(self, event):
if event.type == pygame.QUIT:
self._running = False
def on_loop(self):
pass
def on_render(self):
self._display_surf.fill(self.bgcolor)
self.sprites.draw(self._display_surf)
pygame.display.flip()
def on_cleanup(self):
pygame.quit()
def on_execute(self):
if self.on_init() is False:
self._running = False
while self._running:
for event in pygame.event.get():
self.on_event(event)
self.on_loop()
self.on_render()
self.on_cleanup()
if __name__ == "__main__":
game = App()
game.on_execute()
If you are copying an image with a per pixel alpha format on another pygame.Surface onto another surface, you need to ensure that the target Surface has a per pixel alpha format, too. If the target cannot save the alpha channel, the transparency will be lost. Set the SRCALPHA flag to create a surface with an image format that includes a per-pixel alpha.
self.image = pygame.Surface([128, 128])
self.image = pygame.Surface([128, 128], pygame.SRCALPHA)
class Piece:
class Piece(pygame.sprite.Sprite):
def __init__(self, kind, posx, posy, sheet, color):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image for the piece and fill it with the image
# TO DO: cut the right type of piece from a sheet, preferably before constructing the object
self.image = pygame.Surface([128, 128], pygame.SRCALPHA)
self.image.blit(sheet, [0, 0])
self.kind = kind # piece type, designated with an uppercase letter, eg. Q for queen
self.color = color # W or B for white or black
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
self.rect.x = posx
self.rect.y = posy
This question already has an answer here:
How do I scale a PyGame image (Surface) with respect to its center?
(1 answer)
Closed 2 years ago.
I want to create a game where circles are generated randomly over the surface and start to grow. When 2 circles touch each other the game ends. So, everything is working except the resizing of the sprite during a loop. When I use transform.scale I get something like this:
Then I found transform.smoothscale in the doc. I used changed my code to use this and then it looked like this:
I also tried to use Rect.inflate but this did nothing to my sprite. And I tried Rect.infalte_ip and if I use this, the sprite won't grow it is more likely to move out of the frame. Any ideas on how I can make these Sprite grow in place and that they resize how they should?
class Bubbles(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image_scale = 100
self.image_scale_factor = 1
self.image = resources.BUBBLE_SKIN[0].convert_alpha()
self.image = pygame.transform.scale(self.image, (self.image_scale, self.image_scale))
self.rect = self.image.get_rect()
self.rect.centerx = (random.randrange(Settings.object_range_limit + (self.image_scale//2), (Settings.width - Settings.object_range_limit - self.image_scale)))
self.rect.centery = (random.randrange(Settings.object_range_limit + (self.image_scale//2), (Settings.height - Settings.object_range_limit - self.image_scale)))
self.growth_rate = random.randint(1, 4)
def update(self):
self.image_scale += self.growth_rate
self.image = pygame.transform.scale(self.image, (self.image_scale, self.image_scale))
You must scale the original sprite, instead of gradually scaling the sprite. Every time you scale the sprint, the quality degrades. If you scale the sprite gradually, the quality gets worse and worse. Store the sprite to an attribute orignal_image and scale the orignal_image.
If you want to scale the image by the center of the image, you must update the rect attribute after scaling the image with the new image size.
See How do I scale a PyGame image (Surface) with respect to its center?
class Bubbles(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.image = resources.BUBBLE_SKIN[0].convert_alpha()
self.image = pygame.transform.scale(
self.image, (self.image_scale, self.image_scale))
self.original_image = self.image
def update(self):
self.image_scale += self.growth_rate
self.image = pygame.transform.scale(
self.original_image, (self.image_scale, self.image_scale))
self.rect = self.image.get_rect(center = self.rect.center)
is there a possibilty to place a Sprite on the position, where i clicked?
class sprite_to_place(pygame.sprite.Sprite):
def __init__(self, x_start_position , y_start_position ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("a_picture.png")
self.rect = self.image.get_rect()
self.rect.x = x_start_position # x where I clicked
self.rect.y = y_start_position # y where I clicked
When I initialize the sprite_to_place I would use pygame.mouse.get_pos().
And in the main loop I would place it with:
if event.type == pygame.MOUSEBUTTONDOWN:
sprite_to_place_group.draw(gameDisplay)
But how can I get the position of the sprite, if I want to change its position with def update()? (I use allsprites_group.update())
def update(self, startpos=(x_start_position, y_start_position)): # how can I tell the function where the sprite is on the map?
self.pos = [startpos[0], startpos[1]]
self.rect.x = round(self.pos[0] - cornerpoint[0], 0) #x
self.rect.y = round(self.pos[1] - cornerpoint[1], 0) #y
If I would do it like in my example, it says that x_start_position and y_start_position are not defined.
Thanks!
You store the current position of the Sprite already in self.rect, so you don't need x_start_position and y_start_position.
If you want to store the original starting position you used when creating the Sprite, you'll have to create a member in the initializer:
#TODO: respect naming convention
class sprite_to_place(pygame.sprite.Sprite):
# you can use a single parameter instead of two
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("a_picture.png")
# you can pass the position directly to get_rect to set it's position
self.rect = self.image.get_rect(topleft=pos)
# I don't know if you actually need this
self.start_pos = pos
Then in update:
def update(self):
# current position is self.rect.topleft
# starting position is self.start_pos
# to move the Sprite/Rect, you can also use the move functions
self.rect.move_ip(10, 20) # moves the Sprite 10px vertically and 20px horizontally
I don't know if this is the correct website, but you guys have been so helpful before, I wanted to get your advice on a problem I'm having with Python and Pygame.
I am making a simple game, and only recently begun learning Python (loving it so far) and at the moment, I having a sprite constructor which I am using. This constructor will manage my objects, but I want it to draw either an ellipse or a rectangle based on an argument passed to it.
#My code
class Block(pygame.sprite.Sprite):
#Variables!
speed = 2
indestructible = True
#Constructor
def __init__(self, color, width, height, name, shapeType):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width,height])
self.image.fill(color)
#Choose what to draw
if shapeType == "Ellipse":
pygame.draw.ellipse(self.image,color,[0,0,width,height])
elif shapeType == "Rect":
pygame.draw.rect(self.image,color,[0,0,width,height])
elif shapeType == "":
print("Shape type for ",name," not defined.")
pygame.draw.rect(self.image,color,[0,0,width,height])
#Init the Rect class for sprites
self.rect = self.image.get_rect()
The coding I am using for drawing a square is below:
#Add 'white star' to the list
for i in range(random.randrange(100,200)):
whiteStar = Block(white, 1, 1, "White Star", "Rect")
whiteStar.rect.x = random.randrange(size[0])
whiteStar.rect.y = random.randrange(size[1])
whiteStar.speed = 2
block_list.add(whiteStar)
all_sprites_list.add(whiteStar)
This works wonderfully. It draws a perfect little white square for me. But this doesn't work:
#Create Planet
planet = Block(green, 15,15, "Planet", "Ellipse")
planet.rect.x = random.randrange(size[0])
planet.rect.y = 30
planet.speed = 1
block_list.add(planet)
all_sprites_list.add(planet)
The 'planet' spawns correctly, but it does so as a square. Why is this happening? And how can I fix it? Should I use a bitmap to correct this? Or is my coding wrong?
Just to clarify, I know for a fact that self.rect = self.image.get_rect() does work to draw an ellipse, because the coding below works.
#Not the code I'm using, but this works and proves self.rect = self.image.get_rect() is not the cause
# 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.Surface([width, height])
self.image.fill(white)
self.image.set_colorkey(white)
pygame.draw.ellipse(self.image,color,[0,0,width,height])
# Fetch the rectangle object that has the dimensions of the image
# image.
# Update the position of this object by setting the values
# of rect.x and rect.y
self.rect = self.image.get_rect()
Thankyou for your help. :-)
In the Block constructor, you call self.image.fill(color). That will fill the sprite's entire image with that color, so you get a rectangle.
The example code you have calls self.image.set_colorkey(white) after doing the fill, so that when it gets drawn, the background fill is transparent. That's probably the fastest solution.
You are filling the surface with the given color, and then drawing your shape in the same color. Of course it won't be visible that way, and you just get the solid-coloured surface, which is rectangular.