1

I am making a tank game where you fire bullets at other tanks. But when your bullets hit an enemy, I get an error like this:

Traceback (most recent call last):
  File "...", line 182, in <module>
    if pygame.sprite.spritecollide(i, enemy_group, True):
  File "...\pygame\sprite.py", line 1645, in spritecollide
    default_sprite_collide_func = sprite.rect.colliderect
AttributeError: 'Bullet' object has no attribute 'rect'

Is this a problem in my code, or is this a problem in pygame?

Here is the bullet class: (which, by the way, overloads the projectile class)

class Bullet(Projectile):
    def __init__(self, *, screen, image):
        Projectile.__init__(self)

        self._screen = screen
        self._x = tank.get("x") + 90
        self._y = tank.get("y") - 7
        self._image = pygame.image.load(image)
        self._rect = self._image.get_rect()

    def update(self, speed):
        if self._x >= 800 or self._x <= 0:
            self.kill()

        self._x += speed
        self._rect.center = (self._x, self._y)

    def blit_on_screen(self):
        self._screen.blit(self._image, self._rect)

(The projectile class is just a class with abstract methods waiting to be overloaded by the bullet class.)

And, here is the enemy class:

class Enemy(pygame.sprite.Sprite):
    def __init__(self, *, screen, x, y):
        pygame.sprite.Sprite.__init__(self)

        self._screen = screen
        self._x = x
        self._y = y

        self._image = pygame.image.load("assets/Tank.png")
        self._image = pygame.transform.flip(self._image, True, False)
        self._image = pygame.transform.scale(self._image, (490, 280))

        self._rect = self._image.get_rect()

    def update(self, speed):
        if self._x <= 0 or self._x >= 800:
            self.kill()

        self._x -= speed
        self._rect.center = (self._x, self._y)

    def blit_on_screen(self):
        self._screen.blit(self._image, self._rect)

This is the part where the checking of the colliding bullets and the enemies:

    for i in bullet_group:
        if pygame.sprite.spritecollide(i, enemy_group, True):
            logging.info("bullet and enemy collided")

What is happening here?

Alan Bagel
  • 818
  • 5
  • 24

1 Answers1

0

pygame.sprite.spritecollide uses the .rect attribute to detect collisions. See the documentation of pygame.sprite.spritecollide

[...] If collided is not passed, all sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision.

Hence you have to name the attributes rect instead of _rect:

class Bullet(Projectile):
    def __init__(self, *, screen, image):
        # [...]      
  
        self.rect = self._image.get_rect()
class Enemy(pygame.sprite.Sprite):
    def __init__(self, *, screen, x, y):
        # [...]

        self.rect = self._image.get_rect()

Alternatively, you can create your own function for collision detection. Pass the function to the optional collided argument:

def my_collide(a, b):
    return a._rect.colliderect(b._rect)
if pygame.sprite.spritecollide(i, enemy_group, True, collided = my_collide):
    # [...]
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Oh, thanks! My solution was to create my own way of detecting collision, like thisL for bullet in bullet_group: ```python for enemy in enemy_group: if bullet.get("rect").centerx <= enemy.get("rect").midleft[0]: bullet.kill() enemy.kill() ``` This way, I don't have to rename `self._rect` to `self.rect` Still, thanks! – Alan Bagel Jun 15 '21 at 16:40
  • @AlanBagel This is a very bad solution. You should prefer to create your own collision detection function and pass it to the `collided` argument. – Rabbid76 Jun 15 '21 at 16:41
  • Wow, I didn't know you could pass a `collided` argument! Thanks! – Alan Bagel Jun 15 '21 at 16:42
  • @AlanBagel I've extended the answer. – Rabbid76 Jun 15 '21 at 16:44