Search code examples
pythonpygamesprite

Why won't the remove command work for my Pygame sprite list?


I am making a game in Pygame where the sprite just goes to the end of the screen then teleports back, and obstacles 1-2 obstacles spawn. When 1 obstacle spawns the other one is still there but invisible and ends the game. Even though I killed it and removed it from the sprite list. What is wrong with my code?

import pygame
import random
import time

jump_v = 7     
isjump = False
v = 5
m = 1
o = 2

GRAY = (192, 192, 192)
RED  = (255,   0,   0)
BLACK = (0 ,    0,   0)
class Player(pygame.sprite.Sprite):
    
    def __init__(self, color, x, y, width, weight):
        super().__init__()
        self.color = color
        self.image = pygame.Surface([width, weight])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

    def update(self):
        global o
        self.rect.x += 7

        if self.rect.colliderect(obstacle1.rect):
            self.rect.x = 0
            o = random.randint(0,2)
            if o == 1:
                obstacle1.rect.x = (random.randint(300, 900))
                obstacle2.kill()
                all_sprites_list.remove(obstacle2)
            if o == 2:
                obstacle1.rect.x = (random.randint(300, 900))
                obstacle2.rect.x = (random.randint(300, 900))
                if obstacle2.rect.x - obstacle1.rect.x > 0:
                    while obstacle1.rect.x - obstacle2 .rect.x < 100:
                        obstacle1.rect.x = (random.randint(300, 900))
                        obstacle2.rect.x = (random.randint(300, 900))
        if self.rect.colliderect(obstacle2.rect):
            self.rect.x = 0
            o = random.randint(0,2)
            if o == 1:
                obstacle1.rect.x = (random.randint(300, 900))
                obstacle2.kill()
                all_sprites_list.remove(obstacle2)
            if o == 2:
                obstacle1.rect.x = (random.randint(300, 900))
                obstacle2.rect.x = (random.randint(300, 900))
                if obstacle2.rect.x - obstacle1.rect.x > 0:
                    while obstacle1.rect.x - obstacle2 .rect.x < 100:
                        obstacle1.rect.x = (random.randint(300, 900))
                        obstacle2.rect.x = (random.randint(300, 900))
        if self.rect.x >= 950:
            self.rect.x = 0
            o = random.randint(0,2)
            if o == 1:
                obstacle1.rect.x = (random.randint(300, 900))
                all_sprites_list.remove(obstacle2)
                obstacle2.kill()
            if o == 2:
                obstacle1.rect.x = (random.randint(300, 900))
                obstacle2.rect.x = (random.randint(300, 900))
                if obstacle2.rect.x - obstacle1.rect.x > 0:
                    while obstacle1.rect.x - obstacle2 .rect.x < 100:
                        obstacle1.rect.x = (random.randint(300, 900))
                        obstacle2.rect.x = (random.randint(300, 900))
class Obstacle1(pygame.sprite.Sprite):
    
    def __init__(self, color, x, y, width, weight,):
        super().__init__()
        self.color = color
        self.image = pygame.Surface([width, weight])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y


class Obstacle2(pygame.sprite.Sprite):
    
    def __init__(self, color, x, y, width, weight,):
        super().__init__()
        self.color = color
        self.image = pygame.Surface([width, weight])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
            

                
#main

color_line = (0,0,0)

pygame.init()
screen = pygame.display.set_mode([1000, 500])

all_sprites_list = pygame.sprite.Group()

player_width  = 50
player_weight = 50

player = Player(RED, 0, 413, 50, 50)
if o == 1:
    obstacle1 = Obstacle1(BLACK, random.randint(300, 900), 410 , 25, 100)
    all_sprites_list.add(obstacle1)
if o == 2:
    obstacle1 = Obstacle1(BLACK, random.randint(300, 900), 410 , 25, 100)
    obstacle2 = Obstacle2(BLACK, random.randint(300, 900), 410 , 25, 100)
    all_sprites_list.add(obstacle1)
    all_sprites_list.add(obstacle2)

all_sprites_list.add(player)

clock = pygame.time.Clock()

running = True
while running:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    #clicks

    keys = pygame.key.get_pressed()
    if isjump == False:
        #Up arrow key
        if keys[pygame.K_UP]:
            isjump = True    
            v = jump_v
        #W key
        if keys[pygame.K_w]:
            isjump = True    
            v = jump_v
        #Space bar
        if keys[pygame.K_SPACE]:
            isjump = True    
            v = jump_v
    else:
        m = 1 if v >= 0 else -1
        F = m * (v**2)
        player.rect.y -= F
        
        v -= 1
        if v < -jump_v:
            isjump = False
    
    #updates
    
    all_sprites_list.update()

    #draws
    
    screen.fill((255, 255, 255))

    pygame.display.set_caption('Jump game thing')

    pygame.draw.line(screen, color_line, (0, 500), (1000, 500), 75)
    
    all_sprites_list.draw(screen)

    pygame.display.flip()

    o = random.randint(0, 2)

    clock.tick(30)
    
pygame.quit()

Solution

  • There is no need to call all_sprites_list.remove(). kill removes the sprite from all groups.

    Create a separate pygame.sprite.Group for the obstacles and a function that removes all current obstacles and generates new obstacles:

    all_sprites_list = pygame.sprite.Group()
    obstacle_group = pygame.sprite.Group()
    
    def create_new_obstacles():
        for obstacle in obstacle_group:
            obstacle.kill()
        o = random.randint(0, 1)+1
        if o > 0:
            obstacle1 = Obstacle(BLACK, random.randint(300, 900), 410 , 25, 100)
            obstacle_group.add(obstacle1)
            all_sprites_list.add(obstacle1)
        if o > 1:
            obstacle2 = Obstacle(BLACK, random.randint(300, 900), 410 , 25, 100)
            while abs(obstacle1.rect.x - obstacle2.rect.x) < 100:
                 obstacle2 = Obstacle(BLACK, random.randint(300, 900), 410 , 25, 100)
            obstacle_group.add(obstacle2)
            all_sprites_list.add(obstacle2)
    

    Use pygame.sprite.spritecollide to detect a collision of the player and an obstacle. Create new obstacles when a collision is detected:

    class Player(pygame.sprite.Sprite):
        # [...]
    
        def update(self):
            self.rect.x += 7
            
            if pygame.sprite.spritecollide(self, obstacle_group, False):
                self.rect.x = 0
                create_new_obstacles()
            
            if self.rect.x >= 950:
                self.rect.x = 0
                create_new_obstacles()
    

    The concept of classes is that you have 1 class but multiple objects with the same class type (see Classes). You don't need the classes Obstacle1 and Obstacle2. Obstacle is sufficient.

    Complete example:

    import pygame
    import random
    
    jump_v = 7     
    isjump = False
    v = 5
    m = 1
    
    GRAY = (192, 192, 192)
    RED  = (255,   0,   0)
    BLACK = (0 ,    0,   0)
    
    class Player(pygame.sprite.Sprite):
        
        def __init__(self, color, x, y, width, weight):
            super().__init__()
            self.color = color
            self.image = pygame.Surface([width, weight])
            self.image.fill(color)
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.y = y
    
        def update(self):
            self.rect.x += 7
            
            if pygame.sprite.spritecollide(self, obstacle_group, False):
                self.rect.x = 0
                create_new_obstacles()
            
            if self.rect.x >= 950:
                self.rect.x = 0
                create_new_obstacles()
    
    class Obstacle(pygame.sprite.Sprite): 
        def __init__(self, color, x, y, width, weight,):
            super().__init__()
            self.color = color
            self.image = pygame.Surface([width, weight])
            self.image.fill(color)
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.y = y
    
    #main
    
    color_line = (0,0,0)
    
    pygame.init()
    screen = pygame.display.set_mode([1000, 500])
    
    player_width  = 50
    player_weight = 50
    
    def create_new_obstacles():
        for obstacle in obstacle_group:
            obstacle.kill()
        o = random.randint(0, 1)+1
        if o > 0:
            obstacle1 = Obstacle(BLACK, random.randint(300, 900), 410 , 25, 100)
            obstacle_group.add(obstacle1)
            all_sprites_list.add(obstacle1)
        if o > 1:
            obstacle2 = Obstacle(BLACK, random.randint(300, 900), 410 , 25, 100)
            while abs(obstacle1.rect.x - obstacle2.rect.x) < 100:
                 obstacle2 = Obstacle(BLACK, random.randint(300, 900), 410 , 25, 100)
            obstacle_group.add(obstacle2)
            all_sprites_list.add(obstacle2)
    
    all_sprites_list = pygame.sprite.Group()
    obstacle_group = pygame.sprite.Group()
    player = Player(RED, 0, 413, 50, 50)
    create_new_obstacles()
    all_sprites_list.add(player)
    
    clock = pygame.time.Clock()
    
    running = True
    while running:
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        #clicks
    
        keys = pygame.key.get_pressed()
        if isjump == False:
            #Up arrow key
            if keys[pygame.K_UP]:
                isjump = True    
                v = jump_v
            #W key
            if keys[pygame.K_w]:
                isjump = True    
                v = jump_v
            #Space bar
            if keys[pygame.K_SPACE]:
                isjump = True    
                v = jump_v
        else:
            m = 1 if v >= 0 else -1
            F = m * (v**2)
            player.rect.y -= F
            
            v -= 1
            if v < -jump_v:
                isjump = False
        
        #updates
        
        all_sprites_list.update()
    
        #draws
        
        screen.fill((255, 255, 255))
    
        pygame.display.set_caption('Jump game thing')
    
        pygame.draw.line(screen, color_line, (0, 500), (1000, 500), 75)
        
        all_sprites_list.draw(screen)
    
        pygame.display.flip()
    
        clock.tick(30)
        
    pygame.quit()