0
\$\begingroup\$

I have a game where the player has to dodge asteroids coming towards it, and I would like to incorporate a life system. Unfortunately, when I tried this, it still killed my player immediately. I printed out the player's lives as a test, and it is going from 3 to -7 whenever an asteroid hits it. How would I make it so that only one life is subtracted?

Here is my code:

import pygame, random

from pygame.locals import (
    RLEACCEL,
    K_UP,
    K_DOWN,
    K_SPACE,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.surf = pygame.image.load("ship.png").convert()
        self.surf.set_colorkey((255, 255, 255), RLEACCEL)
        self.rect = self.surf.get_rect()
        self.lives = 3

    def update(self, pressed_keys):
        if pressed_keys[K_UP]:
            self.rect.move_ip(0, -5)
        if pressed_keys[K_DOWN]:
            self.rect.move_ip(0, 5)

        if self.rect.top <= 0:
            self.rect.top = 0

        if self.rect.bottom >= SCREEN_HEIGHT:
            self.rect.bottom = SCREEN_HEIGHT

class Asteroid(pygame.sprite.Sprite):
    def __init__(self):
        super(Asteroid, self).__init__()

        self.surf = pygame.image.load("asteroid.png").convert()
        self.surf.set_colorkey((255, 255, 255), RLEACCEL)
        self.rect = self.surf.get_rect(
            center=(
            random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
            random.randint(0, SCREEN_HEIGHT),
            )
        )
        
        self.speed = random.randint(5,20)

    def update(self):
        self.rect.move_ip(-self.speed, 0)
        if self.rect.right < 0:
            self.kill()

    def remove(self):
        self.kill()


SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600


pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

ADDENEMY = pygame.USEREVENT + 1
pygame.time.set_timer(ADDENEMY, 250)


player = Player()

enemies = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)

clock = pygame.time.Clock()
running = True

while running:
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False

        elif event.type == QUIT:
            running = False

        elif event.type == ADDENEMY:
            new_Asteroid = Asteroid()
            enemies.add(new_Asteroid)
            all_sprites.add(new_Asteroid)

    pressed_keys = pygame.key.get_pressed()
    player.update(pressed_keys)

    enemies.update()

    screen.fill((0,0,0))

    for entity in all_sprites:
        screen.blit(entity.surf, entity.rect)

        if pygame.sprite.spritecollideany(player, enemies):
            player.lives -= 1
            print(player.lives)
            if player.lives == 0:
                player.kill()
                running = False


    pygame.display.flip()
    clock.tick(30)

UPDATE: After trying out the first answer, here is the code for colliding:

        if pygame.sprite.spritecollideany(player, enemies): # Check if collided
            if not new_Asteroid.collided_with_player: # Check if the asteroid has already collided with the Player
                # Substract a life, set the asteroids collided player to True so it does not substract more than one, and play some sfx
                player.lives -= 1
                new_Asteroid.collided_with_player = True
                move_sound.stop()
                hit_sound.play()

                # If there are no lives left
                if player.lives == 0:
                    heart1.kill()
                    player.kill()
                    running = False

(In the asteroid class I have the collided_with_player set to False by default)

\$\endgroup\$
1
  • \$\begingroup\$ I have updated my answer. \$\endgroup\$
    – Vaillancourt
    May 30 at 12:56

1 Answer 1

3
\$\begingroup\$

The player loses many lives because you don't tell your game to ignore collisions after the first frame there is a collision. Since there is a collision detected each frame, another life will be removed each frame until the collision no longer occurs (e.g. the Asteroid went through the Player).

I see two ways to fix it:

  1. Once a collision with an Asteroid is detected, have the Player lose a life and destroy the Asteroid with a great boom! (💥)

  2. If the Asteroid must not be destroyed because of the needs of your game design, add a flag collided_with_player on your Asteroid object (set to False by default), and once a collision with an Asteroid which has the collided_with_player set to False is detected, have the Player lose a life and set collided_with_player to True. This way you'll ignore future collisions between the player and this Asteroid.

Keep in mind that you have a flaw in your code. You're checking for collisions with any enemy, so you are unable to single out which enemy has hit the player (or rather, you're always affecting the last generated enemy). For this, you can change your code to something like this:

for enemy in enemies:
    screen.blit(enemy.surf, enemy.rect)

    if enemy collides with player and not enemy.collided_with_player:
        enemy.collided_with_player = True 
        player.lives -= 1
        print(player.lives)
        if player.lives == 0:
            player.kill()
            running = False

screen.blit(player.surf, player.rect)
\$\endgroup\$
4
  • \$\begingroup\$ I already had thought of 1. , but couldn't get it to work correctly. 2. I tried, and it almost worked, as it substracted the first life well, but when I hit asteroid 2, it substracted two lives at once. Here is the new collision: if not new_Asteroid.collided_with_player: player.lives -= 1 print(player.lives) new_Asteroid.collided_with_player = True if player.lives == 0: player.kill() running = False \$\endgroup\$ May 28 at 16:50
  • \$\begingroup\$ I also set the default state of .collide_with_player to False in the class. \$\endgroup\$ May 28 at 17:04
  • \$\begingroup\$ UPDATE: Sometimes it substracts 2 lives, sometimes 1 \$\endgroup\$ May 28 at 20:41
  • \$\begingroup\$ @CodeWizard777 could you paste your whole updated code somewhere? \$\endgroup\$
    – Vaillancourt
    May 28 at 21:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .