Pygame- Sprite set position with mouseclick - python

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

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

How to add a function with if statements (key movement) in a class (python/pygame)?

I need to program a game in pygame similar to pong, but with 1 player. Although there is one paddle and one ball, I'm required to make a class for the paddle and for the ball. I created the paddle class, drawn it, but I have problem with implementing the if statements for movement. Here's what I tried:
class Paddle():
def __init__(self,x,y,width,height,color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
def Draw(self, screen,):
pygame.draw.rect(window, self.color, [self.x,self.y,self.width,self.height])
def Keys(self, y, height):
keys = pygame.key.get_pressed()
if keys [pygame.K_UP]:
self.y -= 1
if keys [pygame.K_DOWN]:
self.y += 1
and then I added a separate function for the objects from the classes:
def Objects():
paddle = Paddle(1150,250,20,100,black)
paddle.Draw(window)
paddle.Keys(250,100)
Again I want to add key movement in the class (since all paddles should have the same function). I should also mention that I'm not getting any error, but it doesn't work.
You must create the instance of Paddle before the application loop. Pass the object to the Objects function:
def Objects(surf, paddle):
paddle.Draw(surf)
paddle.Keys(250,100)
my_paddle = Paddle(1150,250,20,100,black)
while True:
# [...]
Objects(window, my_paddle)
# [...]

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 do I set a boundary in which my image can go through in Pygame?How do I keep an image from going behind another?

This is the code for my pygame
import pygame
import os
img_path=os.path.join('C:/Desktop/Python Stuff','Angry Birds.jpg')
class pic(object):
def __init__(self):
""" The constructor of the class """
self.image = pygame.image.load(img_path)
# the bird's position
self.x = 0
self.y = 0
def handle_keys(self):
""" Handles Keys """
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN]: # down key
self.y += dist # move down
elif key[pygame.K_UP]: # up key
self.y -= dist # move up
if key[pygame.K_RIGHT]: # right key
self.x += dist # move right
elif key[pygame.K_LEFT]: # left key
self.x -= dist # move left
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, (self.x, self.y))
This is the screen size. Is this where the I should restrict the image's boundaries?
pygame.init()
screen=pygame.display.set_mode([1500,850])
Pic=pic()
pygame.display.set_caption('Angry Birds')
This is the image that I want to have a boundary for
pic=pygame.image.load('Angry Birds.jpg')
keep_going=True
while keep_going:
event=pygame.event.poll()
*emphasized text* if event.type == pygame.QUIT:
pygame.quit()
running = False
Pic.handle_keys()
screen.blit(pic, (-200, 0))
Pic.draw(screen)
This image is what the 'Angry Birds' image is going behind. How do I stop it from going behind this image?
tux=pygame.image.load('Rock Lee.gif')
screen.blit(tux,(500,600))
screen.blit(tux,(500,400))
screen.blit(tux,(500,0))
screen.blit(tux,(900,200))
screen.blit(tux,(900,400))
screen.blit(tux,(900,600))
screen.blit(tux,(1300,0))
screen.blit(tux,(1300,200))
screen.blit(tux,(1300,600))
pygame.display.get_surface([1500,850]).get_size([1500,850])
pygame.display.update()
A) Keep rect on screen
The simplest way would be using Rect.clamp_ip(rect) on a Sprite
screen_size = Rect(1500,850)
# right after when you move the bird
bird.rect.clamp_ip(screen_size)
B) rect on rect collision
# Where .xvel and .yvel are bird's movement per frame
new_rect = bird.rect.move(bird.vxel, bird.yvel)
if not new_rect.collide_rect(other_bird.rect)
bird.rect = new_rect
else
print("Ouch!")
Border collision
An easy way to implement border collision is to just check if the current position is outside the screen, and if it is you move it back. It's easiest done by creating a Rect object from the screen which you could pass in an update method of your class pic (classes should start with capital letter). So start with creating an update method were you pass the screen object.
Also, since the x and y position reference the top left of the image you need to take that in consideration when checking for border collision with the right side and the bottom. Best would be to create attributes width and height instead of what I'm doing below.
def update(self, screen):
"""Method that check border collision for object 'pic'."""
border = screen.get_rect()
width = self.image.get_width()
height = self.image.get_height()
if self.x < border.left:
# You collided with the left side of the border.
# Move your character back to the screen
self.x = border.left
elif self.x > border.right - width:
# You collided with the right side of the border.
# Move your character back to the screen
self.x = border.right - width
if self.y < border.top:
# You collided with the top of the border.
# Move your character back to the screen
self.y = border.top
elif self.y > border.bottom - height:
# You collided with the bottom of the border.
# Move your character back to the screen
self.y = border.bottom - height
All you have to do is call this method every time in your loop, like so:
Pic.handle_keys() # Variables should be all lowercase! Not like it's currently.
Pic.update(screen) # Variables should be all lowercase! Not like it's currently.
Pic.draw(screen) # Variables should be all lowercase! Not like it's currently.
Keep image in front
When blitting to the screen it draws each image on top of each other. In your case you're blitting your character and then the rocks, meaning the rocks always be on top of your character. Changing this is simple, blit the rocks first and the character last and your character will end up in front of your rocks.

Making sprites inside/outside of a class

I've got all my code working on its own. I need to start linking it all to buttons though.
QUESTION: trying to setup multiple buttons as sprites for collision purposes. Don't know how to do it outside of a class.
I have buttons working in seperate classes, but cannot get them to work in the same class for the obvious reason of, the self.image of the second one is overwriting the first one.
class Icons(pygame.sprite.Sprite):
def __init__(self, *args):
pygame.sprite.Sprite.__init__(self, *args)
self.image = pygame.image.load("images/airbrushIC.gif").convert()
self.rect = self.image.get_rect()
ic1 = self.image
self.rect.x = 50
self.rect.y = 490
self.image = pygame.image.load("images/fillIC.gif").convert()
self.rect = self.image.get_rect()
ic2 = self.image
self.rect.x = 10
self.rect.y = 540
def update(self):
pygame.mouse.get_pos()
pygame.mouse.get_pressed()
This code doesn't have to be a class. But I do not know how to make the images a sprite without being inside of a class. Any help would be appriciated thanks!
Instead of Icons you should have a generic Icon class.
Then you can create an instance of Icon for each button.
class Icon(pygame.sprite.Sprite):
def __init__(self, image_name, pos, cb, cb_data, *args):
pygame.sprite.Sprite.__init__(self, *args)
self.image = pygame.image.load("images/" + image_name).convert()
self.rect = self.image.get_rect()
self.rect.x = pos[0]
self.rect.y = pos[1]
this.cb = cb # function to call when button is pressed
this.cb_data = cb_data # data to pass to the function
def pressed():
this.cb(cb_data)
Then in you main function you create the buttons:
ic1 = Icon("airbrushIC.gif", (50, 490), button_pressed, "airbrushIC")
ic2 = Icon("fillIC.gif", (10, 540), button_pressed, "fillIC")
buttons = [ic1, ic2]
def button_pressed(data):
print "Button pressed:" + str(data)
Last, for every mouse down event you look for a button collition:
for b in buttons:
if b.rect.collidepoint(event.pos):
b.pressed()

Categories