Pygame - all objects are drawn to the same location - python

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().

Related

center an object/image rect

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

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

Problems with Sprites Appearing and Collision with Rotated Objects; Pygame object is not iterable

I'm currently trying to make pixel perfect collisions between my pong ball and my player's paddle using the mask and collide_rect functions. I made my own checkCollision function in the pong class which would check for pixel perfect collision. However, right now, I can't even get the Sprites to work or appear on the screen because my "Pong object is not iterable.
Here is my pong class with the important features: (I will post additional code if needed)
class Pong(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = screensize[0] // 2
self.centery = screensize[1] // 2
self.radius = 25
self.rect = pygame.Rect(self.centerx-self.radius,
self.centery-self.radius,
self.radius*2, self.radius*2)
self.pokeimage = pygame.image.load("pokeball.png")
self.pokeimage = pygame.transform.scale(self.pokeimage, (self.radius, self.radius))
#Create the mask
self.mask = pygame.mask.from_surface(self.pokeimage)
def checkCollision(self, player_paddle, ai_paddle):
col = pygame.sprite.collide_rect(self, player_paddle)
return col
def collisionFormula(self, player_paddle, ai_paddle):
if self.checkCollision(self, player_paddle):
def collision_checks(self, player_paddle, ai_paddle):
#if the ball hits the top or bottom of the screen, change the y direction
if self.rect.top <= 0 or self.rect.bottom >= self.screensize[1] - 1:
self.direction[1] *= -1
#if the pong hits the paddles, change how the pong ball moves
if self.rect.colliderect(player_paddle.rect) or self.rect.colliderect(ai_paddle.rect):
self.collisionFormula(player_paddle, ai_paddle)
def update(self, player_paddle, ai_paddle):
self.update_ball_position()
self.reset_ball()
self.collision_checks(player_paddle, ai_paddle)
In my PlayerPaddle class, I do the same mask initialization.
class PlayerPaddle(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = 50
self.centery = screensize[1]//2
self.height = 100
self.width = 20
self.imageMaster = pygame.image.load("naruto.png").convert_alpha()
self.imageMaster = pygame.transform.scale(self.imageMaster, (self.width, self.height))
self.image = self.imageMaster
#mask
self.mask = pygame.mask.from_surface(self.image)
def turnLeft(self):
self.dir += 45
if self.dir > 360:
self.dir = 45
def turnRight(self):
self.dir -= 45
if self.dir < 0:
self.dir = 315
def update(self):
#Rotate functions
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMaster, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
And here is my main function:
def main():
pygame.init()
screensize = (640,700)
screen = pygame.display.set_mode(screensize)
background = pygame.Surface(screen.get_size())
background.fill((0, 255, 0))
clock = pygame.time.Clock()
pong = Pong(screensize)
player_paddle = PlayerPaddle(screensize)
ai_paddle = AIPaddle(screensize)
paddleSprite = pygame.sprite.Group(player_paddle)
pongSprite = pygame.sprite.Group(pong)
while running:
running = True
#object updating phase
ai_paddle.update(pong, player_paddle)
player_paddle.update()
pong.update(player_paddle, ai_paddle)
if pygame.sprite.spritecollide(player_paddle, pong, False, pygame.sprite.collide_mask):
print("Collided")
#rendering phase
ai_paddle.render(screen)
player_paddle.render(screen)
pong.render(screen)
paddleSprite.clear(screen, background)
paddleSprite.update()
paddleSprite.draw(screen)
pongSprite.clear(screen,background)
pongSprite.update()
pongSprite.draw(screen)
pygame.display.flip()
pygame.quit()
main()
I made two "group" objects (the pong and the player_paddle) but I'm not sure why I'm even failing to run the program. Additionally, I know the collision will not work because the pong ball will hit the rectangle of the original image, but not the rotated image, but I'm not sure why that will happen if I use the built in sprite function. Thanks.
Read documentation for spritecollide
Find sprites in a group that intersect another sprite.
spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
Second argument has to be group (pygame.sprite.Group), not single Sprite.
It can be group event with one sprite. But you use pong which is single sprite, not group.
See documentation for collide_mask
Collision detection between two sprites, using masks.
collide_mask(SpriteLeft, SpriteRight) -> point
It checks collision between two sprites using mask.
EDIT: in your code you have problem with
spritecollide(player_paddle, pong,...)
because pong is single Sprite, not Group.
With pong you should use collide_mask
collide_mask(player_paddle, pong)
You can use spritecollidewith pongSprite which is Group
spritecollide(player_paddle, pongSprite,...)`
BTW: you could use better names ie. pong_group instead of pongSprite.
And eventually pong_sprite instead of pong (but pong is OK, too).

How to add a matrix of sprites to the screen in python

I was wondering how to draw a matrix with sprites and specific rows and columns to the screen. Here is my code so far:
rows = 3
cols = 6
choices = [Enemy(), Enemy2()]
def create_enemies():
matrix = [[np.random.choice(choices) for i in range(cols)] for j in range(rows)]
create_enemies()
except I dont know how to draw this matrix with the sprites to the screen. Any help?
Here are my enemy classes also:
class Enemy(pygame.sprite.Sprite):
speed = 2
def __init__(self):
super().__init__()
self.image = pygame.Surface([40, 40])
self.image = pygame.image.load("images/e1.png").convert_alpha()
self.rect = self.image.get_rect(center=(40,40))
self.rect.x = 0
self.rect.y = 0
def update(self):
self.rect.y += 1
class Enemy2(pygame.sprite.Sprite):
speed = 2
def __init__(self):
super().__init__()
self.image = pygame.Surface([40, 40])
self.image = pygame.image.load("images/e2.png").convert_alpha()
self.rect = self.image.get_rect(center=(40,40))
self.rect.x = 0
self.rect.y = 0
def update(self):
self.rect.y += 1
Create a pygame.sprite.Group instance and add the matrix to it:
all_sprites = pygame.sprite.Group()
all_sprites.add(matrix)
To update and draw all contained sprites, just call all_sprites.update() (calls the update method of the sprites) and all_sprites.draw(screen) in your main loop.
Note that your matrix contains only references to the two enemy instances in your choices list. If you want unique sprite instances change your code like so:
choices = [Enemy, Enemy2] # Contains references to the classes now.
# Create new instances in the list comprehension.
matrix = [[random.choice(choices)() for i in range(cols)] for j in range(rows)]
There seems to be no reason to use numpy.

Pygame- Sprite set position with mouseclick

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

Categories