I created a class and each member of this class is a member of my group of sprites. I test the collision between my player and the group with:
pygame.sprite.spritecollide(self,surprise_sprites,False)
and I would like to know which sprite of my group has collided in order to use function from their class.
class Surprise(pygame.sprite.Sprite):
def __init__(self,x,y,win):
pygame.sprite.Sprite.__init__(self, sol_sprites)
pygame.sprite.Sprite.__init__(self, surprise_sprites)
self.width = TILESIZE
self.height = TILESIZE
self.image = Block_surprise
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.y = y
self.x = x
self.win = win
self.exist = True
def update(self):
if self.exist:
self.collision()
win.blit(self.image,(camera.apply_player([self.rect.x]),self.rect.y))
def collision(self):
blocks_hit_list = pygame.sprite.spritecollide(self,player_sprite,False)
if not (blocks_hit_list == []):
self.exist = False
self.image = brick_img
print("TOUCHE")
def i_want_to_execute_a_function_here(self):
pygame.sprite.spritecollide() returns the list of the sprites which collided.
A sprite list can be traversed:
blocks_hit_list = pygame.sprite.spritecollide(self,surprise_sprites,False)
for hit_sprite in blocks_hit_list:
# [...] whatever e.g.
# hit_sprite.myMethod();
Related
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
I am following along with a tutorial on how to detect collisions between the class Player and a sprite group MudballGroup. When setting up the collision in the statement pg.sprite.spritecollide(Player, mudballGroup, False) I get the error type object 'Player' has no attribute 'rect'. I have code from my Player sprite here that shows rect to be defined in the following statement: self.rect = self.image.get_rect(). I don't know why I am getting this error. Please if someone could help.
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.walking = False
self.jumping = False
self.current_frame = 0
self.last_update = 0
self.load_images()
self.image = self.girl_standing[0]
#Isn't this my rect attribute for Player?
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
self.clock = pg.time.Clock()
def shoot(self):
mudball = MUDBALL(self.rect.centerx, self.rect.centery)
print("SHOOT function")
self.game.all_sprites.add(mudball)
mudballGroup = pg.sprite.Group()
mudballGroup.add(mudball)
# Me attempting collision
hits = pg.sprite.spritecollide(self.player, mudballGroup, False)
if hits:
print("HITS!!!!!!!!", hits)
def hailing(self):
jets = JETS()
print("FLYOVER")
self.game.all_sprites.add(jets)
jetsGroup = pg.sprite.Group()
jetsGroup.add(jets)
class MUDBALL(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("SLIMEballGREEN.png")
# self.mudballGroup = pg.sprite.Group()
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedx = -30
def update(self):
self.rect.x += self.speedx
#kill if moves off screen
if self.rect.centerx < 0:
self.kill()
class JETS(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("JETSscaled.png")
#self.jetsGroup = pg.sprite.Group()
self.rect = self.image.get_rect()
self.rect.x = 1366
self.rect.y = 0
self.speedx = -70
def update(self):
self.rect.x += self.speedx
#kill if moves off screen
if self.rect.x + 848 < 0:
self.kill()
You're using the Player class for the collision detection, but you have to use an instance of this class instead.
# Player is the class, but spritecollide needs an instance.
hits = pg.sprite.spritecollide(Player, mudballGroup, False)
To create an instance of the Player class, just write:
# Don't forget to pass the game instance to the `Player`s __init__ method.
player = Player(game)
It also looks odd to me that you define the mudballGroup inside of the shoot method. That means the group will only contain one mudball, but then you could also just check if the rects of the player and mudball collide: player.rect.colliderect(mudball.rect) instead of spritecollide. However, if you want multiple mudballs you need to store the mudballGroup as an attribute of the other class, in the __init__ method write:
self.mudballGroup = pg.sprite.Group()
Edit: Okay, you already have a self.player instance in your game instance. I recommend to define the mudballGroup in the Game class as well and then just pass it and the all_sprites group to the player's shoot method to add a mudball. The collision detection can be done inside the game's update method.
class Game:
def new(self):
# Other code omitted ...
self.mudballGroup = pg.sprite.Group()
def update(self):
# Check if the player is shooting.
if self.player.shooting: # You have to add a `shooting` attribute to player.
# `shoot` just adds a mudball to these groups.
self.player.shoot(self.all_sprites, self.mudballGroup)
# Then detect collisions.
hits = pg.sprite.spritecollide(self.player, self.mudballGroup, False)
if hits:
print("HITS!!!!!!!!", hits)
class Player(pg.sprite.Sprite):
# Pass the sprite groups of the game to the shoot method.
def shoot(self, all_sprites, mudballGroup):
mudball = MUDBALL(self.player.centerx, self.player.centery)
# Add sprite to the passed groups.
all_sprites.add(mudball)
mudballGroup.add(mudball)
Edit 2: Here's the other variant. Pass the two needed sprite groups to the player when you create the instance (you don't have to pass the complete game instance), then set them as attributes of the player.
class Game:
def new(self):
# Other code omitted ...
self.all_sprites = pg.sprite.Group()
self.mudballGroup = pg.sprite.Group()
self.player = Player(self.all_sprites, self.mudballGroup)
def update(self):
# Check if the player is shooting.
if self.player.shooting:
# `shoot` adds a mudball to self.all_sprites & self.mudballGroup.
self.player.shoot()
# Then detect collisions.
hits = pg.sprite.spritecollide(self.player, self.mudballGroup, False)
if hits:
print("HITS!!!!!!!!", hits)
class Player(pg.sprite.Sprite):
def __init__(self, all_sprites, mudballGroup):
# Other code omitted ...
# These two attributes are references to the groups
# that were defined in the Game class.
self.all_sprites = all_sprites
self.mudballGroup = mudballGroup
def shoot(self):
mudball = MUDBALL(self.player.centerx, self.player.centery)
# Add sprite to the passed groups.
self.all_sprites.add(mudball)
self.mudballGroup.add(mudball)
Edit 3: Okay, forget the shooting attribute (it would only be needed for the player instance to check if the user is shooting). You also don't need to call trump's shoot method in the Game class, since you already call it in his update method. So here's the updated code:
class Game(pg.sprite.Sprite):
def new(self):
# Other code omitted ...
self.all_sprites = pg.sprite.Group()
self.mudballGroup = pg.sprite.Group()
# Pass the groups to trump during the instantiation.
# You don't have to pass the complete game instance (self).
self.trump = TRUMP(self.all_sprites, self.mudballGroup)
self.all_sprites.add(self.trump)
def update(self):
hits = pg.sprite.spritecollide(self.player, self.mudballGroup, False)
for collided_sprite in hits:
print("HIT", collided_sprite)
class TRUMP(pg.sprite.Sprite):
def __init__(self, all_sprites, mudballGroup):
# Now set the passed groups as attributes of the trump instance.
# These attributes are references to the same sprite groups as
# in the Game class.
self.all_sprites = all_sprites
self.mudballGroup = mudballGroup
def shoot(self):
print("SHOOT function")
mudball = MUDBALL(self.rect.centerx, self.rect.centery)
# Add the mudball to the groups.
self.all_sprites.add(mudball)
self.mudballGroup.add(mudball)
I am trying to create a sprite class where the user can define and add any number of sprites on to the screen from a random location using this tutorial. However, when I try to run my current program it puts the error
AttributeError: type object 'Sprite' has no attribute 'sprite'
But I dont understand why, all the logic seems correct.
Any suggestions?
Heres my code:
import pygame, sys, random
pygame.init()
black = (0, 0, 0)
image = pygame.image.load("resources/images/img.png")
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 400
sprite_width = 5
sprite_height = 5
sprite_count = 5
# no changes here when using sprite groups
class Sprite(pygame.sprite.Sprite):
def __init__(self, Image, pos):
pygame.sprite.Sprite.__init__(self)
self.image =pygame.image.load("resources/images/img.png")
self.rect = self.image.get_rect()
self.rect.topleft = pos
self.pygame.display.set_caption('Sprite Group Example')
self.clock = pygame.time.Clock()
# this is a how sprite group is created
self.sprite = pygame.sprite.Group()
def update(self):
self.rect.x += 1
self.rect.y += 2
if self.rect.y > SCREEN_HEIGHT:
self.rect.y = -1 * sprtie_height
if self.rect.x > SCREEN_WIDTH:
self.rect.x = -1 * sprite_width
#classmethod
def sprites(self):
for i in range(actor_count):
tmp_x = random.randrange(0, SCREEN_WIDTH)
tmp_y = random.randrange(0, SCREEN_HEIGHT)
# all you have to do is add new sprites to the sprite group
self.sprite.add(Sprite(image, [tmp_x, tmp_y]))
#classmethod
def game_loop(self):
screen = pygame.display.set_mode([640, 400])
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(black)
# to update or blitting just call the groups update or draw
# notice there is no for loop
# this will automatically call the individual sprites update method
self.sprite.update()
self.sprite.draw(screen)
self.pygame.display.update()
self.clock.tick(20)
Sprite.sprites()
Sprite.game_loop()
You define class method (#classmethod) but self.sprite exists only in object/instance not in class - it is created in __init__ when you crate new object/instance.
Remove #classmethod to have object/instance method and have no problem with self.sprite.
Or create sprite outside __init__ to get class attribute
class Sprite(pygame.sprite.Sprite):
sprite = pygame.sprite.Group()
def __init__(self, Image, pos)
# instance variable - value copied from class variable
print self.sprite
# class variable
print self.__class__.sprite
#classmethod
def sprites(self):
# instance variable not exists
# class variable
print self.sprite