Collision does not work for pygame Sprites

I'm trying to make a spaceship game for my Python programming class, and I want to use sprite.spritecollideany to check if my sprite spaceship collides with any of the asteroid sprites in the spriteGroup but no matter what I do, it does not work.

Here is the code:

 import pygame import random pygame.init() screen = pygame.display.set_mode((600, 600)) pygame.display.set_caption("Asteroids and Spaceships") background = pygame.image.load("background.png") background = background.convert() white = 255,255,255 #first I make the asteroids with this class. class asteroids(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y self.width = 30 self.height = 30 self.i1 = pygame.image.load("smallasteroid.png") self.i1 = self.i1.convert() self.i1.set_colorkey(white) self.rect = self.i1.get_rect() self.rect.left = x self.rect.top = y self.i2 = pygame.image.load("smallasteroid2.png") self.i2 = self.i2.convert() self.i2.set_colorkey(white) self.rect = self.i2.get_rect() self.rect.left = x self.rect.top = y self.i3 = pygame.image.load("mediumasteroid.png") self.i3 = self.i3.convert() self.i3.set_colorkey(white) self.rect = self.i3.get_rect() self.rect.left = x self.rect.top = y self.current = 0 def render(self, image_num): if image_num == 1: self.current = 1 if image_num == 2: self.current = 2 if image_num == 3: self.current = 3 def update(self): if self.current == 1: screen.blit(self.i1, (self.x,self.y)) self.y += random.randint(7,11) if self.y > screen.get_height(): self.y = 0 if self.current == 2: screen.blit(self.i2, (self.x,self.y)) self.y += random.randint(5,9) if self.y > screen.get_height(): self.y = 0 if self.current == 3: screen.blit(self.i3, (self.x,self.y)) self.y += random.randint(3,6) if self.y > screen.get_height(): self.y = 0 #and then this is the class for the spaceship class spaceship(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y self.width = 40 self.height = 60 self.image = pygame.image.load("spaceship.png") self.image = self.image.convert() self.image.set_colorkey(white) self.rect = self.image.get_rect() self.rect.left = x self.rect.top = y def update(self): screen.blit(self.image,(self.x,self.y)) if self.y > screen.get_height(): self.y = 0 if self.y < screen.get_height()-610: self.y = screen.get_height() if self.x > screen.get_width(): self.x = 0 if self.x < screen.get_width()-610: self.x = screen.get_width() #main is where I have the game run def main(): x_ship, y_ship = 0,0 player = spaceship(300,550) spriteGroup = pygame.sprite.Group() #my asteroid sprites are grouped here for i in range(25): image_num = random.randint(0,3) asteroid = asteroids(random.randint(0,500),random.randint(0, 200)) asteroid.render(image_num) spriteGroup.add(asteroid) clock = pygame.time.Clock() keepGoing = True while keepGoing: #this is what I tried to use to check for collisions. I know it not working but I don't know why. if pygame.sprite.spritecollideany(player,spriteGroup): #the program quits on collision. pygame.quit() for event in pygame.event.get(): if (event.type == pygame.QUIT): keepGoing = False elif event.type == pygame.KEYUP: if event.key == pygame.K_ESCAPE: keepGoing = False if (event.type == pygame.KEYDOWN): if (event.key == pygame.K_LEFT): x_ship = -4 if (event.key == pygame.K_RIGHT): x_ship = 4 if (event.key == pygame.K_UP): y_ship = -4 if (event.key == pygame.K_DOWN): y_ship = 4 if (event.type==pygame.KEYUP): if (event.key==pygame.K_LEFT): x_ship = 0 if (event.key==pygame.K_RIGHT): x_ship = 0 if (event.key==pygame.K_UP): y_ship = 0 if (event.key==pygame.K_DOWN): y_ship = 0 player.x += x_ship player.y += y_ship screen.blit(background, (0,0)) spriteGroup.clear(screen, background) player.update() spriteGroup.update() clock.tick(50) pygame.display.flip() pygame.quit() main() 

I cannot understand why the collision is not working.

+5
source share
1 answer

Your problem is that neither the spaceship classes nor asteroids revise their own rect (their hitboxes) to update , and their x and y attributes have no direct or automatic connection to the location of this rectangle. If you add something like self.rect.topleft = self.x, self.y to the end of your update function for both classes, then their respective rectangles - that is, your hitboxes - will move to where they should be, instead of to stay in their initialized places, which in this case is equal to (300 550) for player and ... some semi-random bias for each asteroid (I'm not sure where exactly: all I did was to play your code carelessly, and then check the bunch, I apologize for not finding the exact origin of the problem ...)

In any case, the short answer is that although you have a current check for the x and y locations for each sprite, you did not tell pygame to actually apply that location to the hitbox, and sprite.spritecollideany always Falsey, because that the blows themselves were not directly related.

Entering self.rect.topleft = self.x, self.y at the end of each of your sprite classes update will fix it. (Make sure this line is at the end of the function and at the lowest indent level in def update(self) !)

EDIT

Alternatively, instead of adding the above code to update you can replace player.x = x_ship and player.y = y_ship in your main loop with something like:

 while keepGoing: ... player.rect.move_ip(x_ship, y_ship) # move the ship rect player.x, player.y = player.rect.topleft # update it x and y, if you use these elsewhere for item in spriteGroup: # update the rects for your asteroid objects item.rect.topleft = item.x, item.y 

I would use the update -altering solution, as there is a really good chance that this solution will cause you grief when a player approaches the edges of the playing field. However, another opportunity for you to consider.

As a suggestion, you can redefine <Sprite>.x and <Sprite>.y as property , which return self.rect.left and return self.rect.top respectively, so the x and y values ​​are bound to the top of your hitbox. The retina is also even.

I'm not sure how you can use these options in the future; you may be able to completely destroy them if you want, and use rect s locators instead. Food for thought!

Note:

I also assume that all sprite x and y attributes refer to the upper left of its rect . If this point should be somewhere else (for example, in the center), you may need to make adjustments to this code if you decide to use it.

+5
source

Source: https://habr.com/ru/post/1209197/


All Articles