center an object/image rect - python

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

Related

Pygame - all objects are drawn to the same location

i have a class to draw objects on the screen
My Class:
class Sword():
def __init__(self, image, rect, speed, center):
self.image = image
self.rect = rect
self.rect.x,self.rect.y = center #set x and y position
self.speed = speed
self.alive = True
def live(self, enemy): #check if the image touches any object
if self.rect.colliderect(enemy):
self.alive = False
mixer.music.play()
def update(self): #move object
self.rect.x += self.speed
def draw(self, surface): #display object
if self.alive:
surface.blit(self.image, (self.rect))
and i tried to add few objects using a loop:
extend = []#object array
liste = [100,200,300]#object points x and y
image= pygame.image.load('sword.png')
rect = image.get_rect()
for i in range(3):
extend.append(Sword(image,rect,1,(liste[i],liste[i])))
while True:
for tile in extend:
tile.draw(display)
and inside my main loop I called this list but it drew all the images based on the last position Exp:rect<300,300,120,40>
Why does it draw all objects in the same location?
It looks like rect is a single object, so as you update it the last coordinates win. You need to make a copy:
from copy import copy
rect = image.get_rect()
for i in range(3):
extend.append(Sword(image,copy(rect),1,(liste[i],liste[i])))
In your Sword class you keep updating the x and y attributes of the same instance:
self.rect = rect
self.rect.x,self.rect.y = center #set x and y position
The rect is the same instance: the one from image.get_rect().

Transparency in pygame sprites

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

How to display random sprite colour on pygame

I want to be able to create a function where if the player collects a point they will change into a random colour out of an option of 4 colours. Im planning to call the changeColour() function once the sprite collides with the point in the main loop ,so the sprite changes colours, but i cant get the sprite to change colour. (note : this is part of the code using pygame)
/// the function used for the colours
def randomColour(): # function for random colour
rand = random.randint(0,3) # random number from 0-3
if(rand == 0): # different numbers will return different colours
return PURPLE # 0 returns purple
elif(rand == 1):
return RED # 1 returns Red
elif(rand == 2):
return TEAL # 2 returns Teal
elif(rand == 3):
return YELLOW # 3 rerurns Yellow
r_colour = randomColour()
/// this cube class is whats used to create the player sprite and food item
class cube(object):
rows = 20 # used to make grdis and draw later
w = 500 # will using squares , this will also be use to calculate space
def __init__(self,start,dirnx=1,dirny=0,color=(r_colour)):
self.pos = start
self.dirnx = 1 # so that player sprite can always be moving
self.dirny = 0
self.color = color # the color of the cube
def draw(self, surface, eyes=False): # draws the cube object used for both the player and the snack
dis = self.w // self.rows
i = self.pos[0]
j = self.pos[1]
pygame.draw.rect(surface, self.color, (i*dis+1,j*dis+1, dis-2, dis-2))
if eyes: # eyes for the player sprite to be drawn on
centre = dis//2
radius = 3
circleMiddle = (i*dis+centre-radius,j*dis+8) # two circles drawn on as eyes / size of eyes
circleMiddle2 = (i*dis + dis -radius*2, j*dis+8)
pygame.draw.circle(surface, (WHITE), circleMiddle, radius) # draws eyes
pygame.draw.circle(surface, (WHITE), circleMiddle2, radius)
/// player object
class player(object): # new class which will contain the contents of the player
body = []
turns = {}
def __init__(self , color, pos):
self.color = randomColour()
self.head = cube(pos) # uses the position of the player to navigate
self.body.append(self.head)
self.dirnx = 0
self.dirny = 1 # starts off with movement / player always moving.
def draw(self, surface): # draw the player onto the screen
for i, c in enumerate(self.body):
if i ==0: # draws the eyes of the player sprite so player can identify the sprite
c.draw(surface, True)
/// redraw window function and the main loop
def redrawWindow(surface): # this will let the screen get updated every time its called
global rows, width, p, snack
surface.fill((BLACK)) # Fills the screen with colour black
p.draw(surface)
snack.draw(surface) # draws snack item
drawGrid(width,rows, surface) # Will draw our grid lines
pygame.display.update()
def main():
global width, rows, p, snack
width = 500
rows = 20
win = pygame.display.set_mode((width, width)) # draws the window for the game
pygame.display.set_caption("Colour Run") # changes name of the window
p = player((randomColour()), (10,10)) # the player
snack = cube(randomSnack(rows, p), color=(0,0,255)) # the point to be collected
run = True # marks runas true so game state will be running
clock = pygame.time.Clock()
while run:
pygame.time.delay(50) #
clock.tick(8) #
p.move() # allows for the player to move
if p.body[0].pos == snack.pos: # checks if player has collided with the point
p.changeColour() # changes the colour of the sprite /// trying to use this
snack = cube(randomSnack(rows, p), color=(0,0,255)) # respawns the point
/// note this isnt the whole code only part of it
I don't have much experience with pygames so there might be something I'm missing here, but your Player class does not have a .changeColour() method ?
If that's the issue then you could just add the method like this :
class player(object): # new class which will contain the contents of the player
body = []
turns = {}
def __init__(self , color, pos):
self.color = randomColour()
self.head = cube(pos) # uses the position of the player to navigate
self.body.append(self.head)
self.dirnx = 0
self.dirny = 1 # starts off with movement / player always moving.
def changeColour(self):
self.color = randomColour()
You can also clean your randomColour() function like so:
def randomColour():
rand = random.randint(0,3)
colors = (PURPLE, RED, TEAL, YELLOW)
return colors[rand]
Note that your function can return you the same color as the one your Player had before change.

builtins.AttributeError: 'module' object has no attribute 'one_tile'

HI I keep getting that error message from the line below
pygame.one_tile.blit(self.i_list[img_index], pos)
and this is from the function below
def create_grid(self, grid_size):
self.grid = [ ]
tile_width = self.surface.get_width() / grid_size
tile_height = self.surface.get_height() / grid_size
# this for loop creates each row in our grid
for i in range(grid_size):
one_row = [ ]
img_index = 0
for j in range(grid_size):
y = i * tile_height
x = j * tile_width
pos = (x,y)
one_tile = Tile(pos, tile_width, tile_height, self.surface)
pygame.one_tile.blit(self.i_list[img_index], pos)
img_index += 1
one_row.append(one_tile)
self.grid.append(one_row)
I'm writing a code for the memory game version 1 (the game that has 8 pairs of images and you need to memorize which image was on which card and match a pair) and I keep get that error message but I don;t really know what I should do to solve it. Any help would be appreciated very much. Thank you!!
and my full code is
import pygame, random
# User-defined functions
def main():
# initialize all pygame modules (some need initialization)
pygame.init()
# create a pygame display window
pygame.display.set_mode((500, 400))
# set the title of the display window
pygame.display.set_caption('A template for graphical games with two moving dots')
# get the display surface
w_surface = pygame.display.get_surface()
# create a game object
game = Game(w_surface)
# start the main game loop by calling the play method on the game object
game.play()
# quit pygame and clean up the pygame window
pygame.quit()
# User-defined classes
class Game:
# An object in this class represents a complete game.
def __init__(self, surface):
# Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# === objects that are part of every game that we will discuss
self.surface = surface
self.bg_color = pygame.Color('black')
self.FPS = 60
self.game_Clock = pygame.time.Clock()
self.close_clicked = False
self.continue_game = True
# === game specific objects
self.max_frames = 150
self.frame_counter = 0
self.i_list = []
self.images = ["image1.bmp", "image2.bmp", "image3.bmp", "image4.bmp", "image5.bmp", "image6.bmp", "image7.bmp", "image8.bmp"]
for i in self.images:
pygame.image.load(i)
self.i_list.append(i)
self.i_list.append(i)
random.shuffle(self.i_list)
self.create_grid(4)
def create_grid(self, grid_size):
self.grid = [ ]
tile_width = self.surface.get_width() / grid_size
tile_height = self.surface.get_height() / grid_size
# this for loop creates each row in our grid
for i in range(grid_size):
one_row = [ ]
img_index = 0
for j in range(grid_size):
y = i * tile_height
x = j * tile_width
pos = (x,y)
one_tile = Tile(pos, tile_width, tile_height, self.surface)
pygame.one_tile.blit(self.i_list[img_index], pos)
img_index += 1
one_row.append(one_tile)
self.grid.append(one_row)
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second
def handle_events(self):
# Handle each user event by changing the game state appropriately.
# - self is the Game whose events will be handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
def draw(self):
# Draw all game objects.
# - self is the Game to draw
self.surface.fill(self.bg_color) # clear the display surface first
for row in self.grid:
for tile in row:
tile.draw()
pygame.display.update()
def update(self):
# Update the game objects for the next frame.
# - self is the Game to update
pass
def decide_continue(self):
# Check and remember if the game should continue
# - self is the Game to check
return True
class Tile:
# A tile represents one location on a grid. Tiles hold content
# (in this case, an X or an O).
def __init__(self, screen_position, width, height, surface):
# initialize one instance of our Tile class. Tiles represent
# one 'position' in our tic-tac-toe board.
# - self: the tile being initialized
# - screen_position: the [x, y] coordinates to draw the tile at
# - surface: the surface on which to draw the tile
# - height: the height of the tile when it is drawn
# - width: the width of the tile when it is drawn
self.screen_position = screen_position
self.surface = surface
self.content = ''
self.height = height
self.width = width
def draw(self):
# draw the contents of a tile to its surface.
tile_boundary = pygame.Rect(self.screen_position[0],
self.screen_position[1],
self.width,
self.height)
pygame.draw.rect(self.surface, pygame.Color("white"), tile_boundary, 2)
main()
I recommend to add an .image attribute and .visible attribute to the class Tile. Each tile knows the associated image and has a state if the image in on the tile is visible:
class Tile:
# A tile represents one location on a grid. Tiles hold content
# (in this case, an X or an O).
def __init__(self, screen_position, width, height, surface, image):
# initialize one instance of our Tile class. Tiles represent
# one 'position' in our tic-tac-toe board.
# - self: the tile being initialized
# - screen_position: the [x, y] coordinates to draw the tile at
# - surface: the surface on which to draw the tile
# - height: the height of the tile when it is drawn
# - width: the width of the tile when it is drawn
self.screen_position = screen_position
self.surface = surface
self.content = ''
self.height = height
self.width = width
self.image = image
self.visible = False
def show(self, visible):
self.visible = visible
def draw(self):
# draw the contents of a tile to its surface.
tile_boundary = pygame.Rect(self.screen_position[0],
self.screen_position[1],
self.width,
self.height)
pygame.draw.rect(self.surface, pygame.Color("white"), tile_boundary, 2)
if self.visible:
img_rect = self.image.get_rect(center = tile_boundary.center)
self.surface.blit(self.image, img_rect.topleft)
To create an image list, you've to load the image. The return value of pygame.image.load is a pygame.Surface object which can be append to i_list:
self.i_list = []
self.images = ["image1.bmp", "image2.bmp", "image3.bmp", "image4.bmp", "image5.bmp", "image6.bmp", "image7.bmp", "image8.bmp"]
for imgname in self.images:
img = pygame.image.load(imgname)
self.i_list.append(img)
random.shuffle(self.i_list)
Pass the image to the constructor of Tile:
for i in range(grid_size):
one_row = [ ]
img_index = 0
for j in range(grid_size):
pos = (j * tile_width, i * tile_height)
one_tile = Tile(pos, tile_width, tile_height, self.surface, self.i_list[img_index])
img_index += 1
one_row.append(one_tile)
self.grid.append(one_row)
Note, for debug reasons you can all the state of all the images "visible" (self.visible = True in the constructor of Tiles).

Controlling pygame animation through text input

I need to create a fighting game that gives prompts and accepts input through text, such as a raw input and then performs the animation, while still have the characters animated, e.g. moving back and forth in a ready to fight stance. How would I go about this?
Please note that this is not going to be your typical answer. StackOverflow is to help after all that you can do on your part when you are stuck, it's not meant as a place to come for code, but since I'm assuming other people new to programming will also be confused on things such as these. So I'm going to write some code, and some psuedo code, just so that you get the just of what you would do in such a scenario.
# TODO put your imports up here
pygame.init()
clock = pygame.time.Clock()
gameSurface = pygame.display.set_mode((600, 400)) # 2/3 aspect ratio
FPS = 40 # Set to your own Frames per second
class Animator:
def __init__(self, surface, rows, cols, time_between_frames, on_finish):
self.images = []
self.current_image = 0
self.time_between_frames = time_between_frames # time animator waits before changing surface
self.current_time # tracks time for frames to change
self.on_finish = on_finish # function to call when animation finishes
surf_width = (surface.get_width() / cols) # calculate width
surf_height = (surface.get_height() / rows) # calculate height
for x in range(cols):
for y in range(rows):
surf = pygame.Surface(surface.get_size()) # temp surface
from_rect = pygame.Rect(x * surf_width, y * surf_height, surf_width, surf_height) # rect to blit from
surf.blit(surface, (0,0), from_rect) # draw to temp surface
self.images.append(surf) # add temp surface to the images list
def update(delta):
self.current_time += delta # update current time
if (self.current_time >= self.time_between_frames): # if time to switch surfaces
self.current_time -= self.time_between_frames # take away time from current time
self.current_image += 1 # change image
if self.current_image >= len(self.images): # if current image would throw an out of bounds exception
self.current_image = 0 # reset the current image to the first
def get_frame(self):
return self.images[self.current_image]
class Player:
resting = 0
abdomenKick = 1
def __init__(self, x, y):
self.x = x
self.y = y
self.action = Player.resting
self.restingAnimation = Animation(pygame.image.load("resting.png"), 2, 3, 500)
self.abdomenKickAnimation = Animation(pygame.image.load("abdomenKick.png"), 4, 6, 50)
self.currentAnimation = self.restingAnimation
def update(self, delta):
self.currentAnimation.update(delta)
def draw(self, surface):
surface.blit(self.currentAnimation.get_frame(), (self.x, self.y))
def abdomenKick(self):
self.currentAnimation = self.restingAnimation
class Game:
def __init__(self):
self.player = Player()
def update(self, delta):
self.player.update(delta)
def draw_screen(self, surface):
self.player.draw(surface)
def gameLoop():
game = Game()
while True:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == A:
game.player.abdomenKick() #Or whatever move you have
game.update(clock.get_rawtime())
game.draw_screen()
clock.tick(FPS)
So here is just a brief showcase you can call it of what this might look like.

Categories