Get the position of the collided sprite - python

I need to get the position of the last sprite that collided between 2 groups. All the sprites in a group have the same name. here is the concentrate code:
rmog = pygame.sprite.Group()
rmg = pygame.sprite.Group()
class rmissileh(pygame.sprite.Sprite):
def __init__(self, image, speed):
pygame.sprite.Sprite.__init__(self)
self.speed = -10
self.speedx = 0
self.image = redm
self.rect = image.get_rect().move(s.rect.x+26,s.rect.y)
def move(self):
self.rect = self.rect.move(self.speedx, self.speed)redshot =
rmissileh(redm, -10 )
rmg.add(redshot)
class crmonstre(pygame.sprite.Sprite):
def __init__(self, image, speed):
pygame.sprite.Sprite.__init__(self)
self.speedy = 2
self.speedx = 1
self.image = rmonstre
self.step = 600
self.rect = rmonstre.get_rect().move(500,-100)
def move(self):
self.rect = self.rect.move(self.speedx,self.speedy)
redmonstre = crmonstre(rmonstre,1)
rmog.add(redmonstre)
redmonstre = crmonstre(rmonstre,1)
rmog.add(redmonstre)
redmonstre = crmonstre(rmonstre,1)# I added only three but there is many more overtime.
rmog.add(redmonstre)
while stop:
for event in pygame.event.get():
if event.type ==pygame.QUIT:
stop = False
for redmonstre in rmog:
fenetre.blit(fond, redmonstre.rect, redmonstre.rect)
redmonstre.move()
fenetre.blit(redmonstre.image,redmonstre.rect)
if pygame.sprite.groupcollide(rmg, rmog,True ,True):
fenetre.blit(fond, (0,0))
pygame.display.update()
pygame.quit()
quit
As you can see when a redmonster collide with a redshot i have to blit the whole background(named "fond") because i don't know his position, if I print(redmonstre.rect.x) that will print the x position of the last object that i created and not the position of the one that collided.
And also in the while loop to blit a moving object i do :
for redmonstre in rmog:
fenetre.blit(fond, redmonstre.rect, redmonstre.rect)
redmonstre.move()
fenetre.blit(redmonstre.image,redmonstre.rect)
i have many many for loop like that in my code and it doesn't feel right if you have an other way to do it please let me know.

I don't know how to find just the last collision. But here is how you could find all of the collisions, and hopefully that's a step in the right direction.
There are two ways you could do this. The first way would be to iterate through the list of sprites yourself. That would look kind of like this:
for sprite in rmg:
#Note that spritecollide will remove every sprite that collides from
#rmog, but it will not remove 'sprite' from rmg.
if (pygame.sprite.spritecollide(sprite, rmog, True)):
print (sprite.x)
rmg.remove(sprite)
spritecollide docs.
Here is the other way you could do it.
Look at the documentation for pygame.sprite.groupcollide. You'll see that it returns a dictionary of all the collisions. The dictionary will have all of the sprites from group 1 (which is rmg) as the keys, and the value for each key will be a list of every sprite from group 2 (which is rmog) that collided with it. So then to find every sprite that collided from group one, you could do:
dict = pygame.sprite.groupcollide(rmg, rmog,True ,True)
for sprite in dict:
if dict[sprite]:
print (sprite.x)
Also, a couple general tips that are unrelated to your problem,
Don't do this:
redmonstre = crmonstre(rmonstre,1)
rmog.add(redmonstre)
redmonstre = crmonstre(rmonstre,1)
rmog.add(redmonstre)
redmonstre = crmonstre(rmonstre,1)
rmog.add(redmonstre)
There is no need to have a name for each redmonstre, and it will only make it more confusing to have them all named the same. Instead, just add them directly to the list:
rmog.add(crmonstre(rmonstre, 1))
rmog.add(crmonstre(rmonstre, 1))
rmog.add(crmonstre(rmonstre, 1))
And don't do this:
for redmonstre in rmog:
fenetre.blit(fond, redmonstre.rect, redmonstre.rect)
redmonstre.move()
fenetre.blit(redmonstre.image,redmonstre.rect)
Blitting "fond" takes a (relatively) long time. If you blit it for every redmonstre, you will only see the last one. Just blit it once before the loop.
fenetre.blit(fond, redmonstre.rect, redmonstre.rect)
for redmonstre in rmog:
redmonstre.move()
fenetre.blit(redmonstre.image,redmonstre.rect)

Related

Having problems creating an animation from a sprite sheet in pygame

I am relatively new to Python and started messing around with pygame a few days ago. I made a sprite sheet in photoshop and I am trying to use it to animate the player in the game. The sprite sheet has 8 images, 4 for walking left and 4 for walking right. I want to show these animations at the correct time. I have tried many different methods I've found online but I haven't managed to get any working and I am still quite confused about it.
This is the Char class that controls the character's movement and sprite. I tried indexing the sprite and changing the index in the update method but I understand this isn't possible but I've left it in to show what I was trying to do. Any help would be appreciated!
import pygame, sys
from pygame.sprite import Sprite
class Char(Sprite):
def __init__(self, pf_game):
"""initialise char and set its starting location"""
self.screen = pf_game.screen
self.settings = pf_game.settings
self.screen_rect = pf_game.screen.get_rect()
self.dog_sprite = pygame.image.load('images/dog_sprite_sheet.bmp').convert()
self.current_sprite = 0
self.dog_image = self.dog_sprite[self.current_sprite]
self.rect = self.dog_image.get_rect()
# start new char at the bottom of centre of the screen
self.rect.midbottom = self.screen_rect.midbottom
#store a decimal value for the ships horizontal position
self.x = float(self.rect.x)
self.y = float(self.rect.y)
#movement flags
self.moving_right = False
self.moving_left = False
self.is_jump = False
def update(self):
"""Update the chars position based on movement flags"""
#update the chars x value and create animation for moving right
if self.moving_right and self.rect.right<self.screen_rect.right:
if self.current_sprite == 7:
self.current_sprite = 4
self.dog_image = self.dog_sprite[self.current_sprite]
self.current_sprite+=1
self.x+= self.settings.char_speed
#update the chars x value and create animation for moving left
if self.moving_left and self.rect.left>0:
if self.current_sprite == 3:
self.current_sprite = 0
self.dog_image = self.dog_sprite[self.current_sprite]
self.current_sprite+=1
self.x-= self.settings.char_speed
#update rect object from self.x
self.rect.x = self.x
self.rect.y = self.y
def blitme(self):
"""Draw the char at its curretn loaction"""
self.screen.blit(self.dog_image, self.rect)
edit:
I found a spritesheet class that seems to do what I want on http://www.pygame.org/wiki/Spritesheet. I am running into an error though when I try and input the coordinates. I get the error images_at() got multiple values for argument 'colorkey'. When i remove the colorkey I get the error images_at() takes from 2 to 3 positional arguments but 5 were given.
This is the code I am using.
self.dog_sprite=spritesheet.spritesheet('dog_sprite_sheet.bmp')
self.left_images=[]
self.right_images=[]
self.left_images=self.dog_sprite.images_at((0,0,19,19),(20,0,19,19),(39,0,19,19),(58,0,19,19), colorkey=(255, 255, 255))
self.right_images=self.dog_sprite.images_at((77,0,19,19),(96,0,19,19),(115,0,19,19),(134,0,19,19), colorkey=(255, 255, 255))
this may help:
def anim(anim_count):
if anim_count < 6:
screen.blit('anim1.png', player_position)
if anim_count > 6 and anim_count < 12:
screen.blit('anim2.png', player_position)
you should call anim at every frame, and then apply 1 to anim_count

Is there a way to change my_sprite or my_group to change the animation displayed?

I'm making animated sprites in pygame, and would like help finding a way to flip from one to the other? The current code looks something like this:
class normal(pygame.sprite.Sprite):
def __init__(self):
#etc, list of images to create the animation
class tall(pygame.sprite.Sprite):
def __init__(self):
#rinse and repeat with a different set of images
I already have an idea for how to trigger the change via keystroke. But I'm not sure which variable to change, and to what. When I try to change with the following code, nothing happens
fps = 25
pygame.init()
my_sprite = normal()
my_group = pygame.sprite.Group(my_sprite)
#etc until we get to the part where it changes
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if my_sprite == normal():
my_sprite = tall()
fps = 30
else:
my_sprite = normal()
fps = 25
I'm not sure exactly what isn't working in my code as it doesn't come back with an error. Can someone point me in the right direction?
It's not working because when the code calls normal() it's creating a new instance of an object. So the call:
if my_sprite == normal():
Is saying "is my existing sprite object == this new sprite object", which is never true. You can use the python function to type() of the object to do the same thing, or add your own type-function as I have presented in the code below.
I would track the state of the sprite inside the class, and use some functions grow() and shrink() to change the size automatically.
class GrowingSprite( pygame.sprite.Sprite, x, y ):
def __init__( self ):
#etc, list of images to create the animation
self.image_short = ... # load "short" image
self.image_tall = ... # load "tall" image
# Set the initial state
self.image = self.image_short # start short
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.state = 'short'
def getState( self ):
return self.state
def grow( self ):
self.state = 'tall'
self.image = self.image_tall
current_x = self.rect.centerx # preserve existing location
current_y = self.rect.centery
self.rect = self.image.get_rect()
self.rect.center = ( current_x, current_y )
def shrink( self ):
self.state = 'short'
self.image = self.image_short
current_x = self.rect.centerx # preserve existing location
current_y = self.rect.centery
self.rect = self.image.get_rect()
self.rect.center = ( current_x, current_y )
I found a workaround that is a bit redundant, but it makes it possible to expand it in case there are more groups
It requires making two groups that can be pulled back and fourth, and changing the foo.draw(screen) to the new group. This is how it looks
nml_sprite = normal()
tal_sprite = tall()
tg = 1 #this is nothing more than a switch to trigger the change
tal_group = pygame.sprite.Group(tal_sprite)
nml_group = pygame.sprite.Group(nml_sprite)
cursprite = nml_group #this variable determines which sprite set is loaded
...
...
if event.key == pygame.K_RETURN:
if tg == 1:
curspt = tal_group
tg = 2
else:
curspt = nml_group
tg = 2
...
...
nml_group.update()
tal_group.update()
curspt.draw(screen)
...

pygame and tiled map collsions

I am trying to make the player sprite stop falling on the platform rect top. I have tried a lot of things over the past two days and I am more lost then I've ever been. Thank you so much. I am using tmx to load a tiled map. I add a Platform object every the name is detected and than I add that to a list. I enumerate through the list to check collision using sprite.collide(self.player, plat). This doesn't work.
def update(self):
for plat in self.platList:
self.hits = pg.sprite.spritecollide(self.player,self.platList,False)
if self.hits:
if self.player.pos.y > self.hits[0].y:
self.player.pos.y = self.hits[0].rect.top
for tile in self.map.tmxdata.objects:
if tile.name == "player":
self.player = Player(self,tile.x,tile.y)
if tile.name == "Platform":
self.platList.append(Platform(self, tile.x, tile.y,tile.width,tile.height))
class Platform(pg.sprite.Sprite):
def __init__(self,game,x,y,width,height):
self.game = game
pg.sprite.Sprite.__init__(self)
self.rect = pg.Rect(x,y,width,height)
self.y = y
self.rect.x = x
self.rect.y = y
I guess your problem is that you don't use the player's rect attribute to define the position of the player. You're using pygame's sprites, so use them the way it is intended.
All pygame functions that deal with sprites (e.g. spritecollide) will use the rect attribute, but in your code the player class has an additional pos attribute, and Platform also has a y attribute.
Remove them, and use rect exclusively to store the size and position of your sprites.
When you want to move a sprite, just change its rect (e.g. with move_ip etc.) or its attributes.

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

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