1

I'm building a shoot'em up on python using pygame but I encountered a problem. I have been trying to solve for a while now. This code runs smoothly until when the asteroid is destroyed, it's supposed to spawn 3 smaller asteroids but instead it continuously spawn a lot of it. Then after a seconds it gives me an error

small_asteroid.Rect.move_ip(small_asteroid.xvel, small_asteroid.yvel)
UnboundLocalError: local variable 'small_asteroid' referenced before assignment
asteroid Destroyed

The way I keep tracks of things like Bullet, Asteroid and Smaller Asteroid is that they're in a separate lists it will check the lists every time. What's an efficient way of tracking things like bullet, asteroid etc.? What I'm doing Wrong? I don't know what I need to show so I posted the whole code instead.


pygame.init()

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 35
INITIAL_PLAYER_X = 400 
INITIAL_PLAYER_Y = 340

Bullet_Velocity = 5
Bullet_Reload_Time = 0.25
Bullet_Damage = 25
List_Of_Asteroids = []
List_Of_Small_Asteroids = []
fpsClock = pygame.time.Clock()
background_colour = (128, 128, 128)
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.flip()

Player_Img_1 = pygame.image.load('assets/Ship_1.png')
Player_Img_2 = pygame.image.load('assets/Ship_2.png')
Player_Img_3 = pygame.image.load('assets/Ship_3.png')
Player_Img_4 = pygame.image.load('assets/Ship_4.png')
Bullet_Img = pygame.image.load('assets/bullet.png')
Big_Asteroid_1 = pygame.image.load('assets/big_asteroid_1.png')
Small_1_Asteroid_1 = pygame.image.load('assets/small_1_asteroid_1.png') 
Small_2_Asteroid_1 = pygame.image.load('assets/small_2_asteroid_1.png') 
Small_3_Asteroid_1 = pygame.image.load('assets/small_3_asteroid_1.png')

class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.Initial_x = x
        self.Initial_y = y
        self.Images = [Player_Img_1, Player_Img_2, Player_Img_3, Player_Img_4]
        self.Bullet_Reload_Time = 0.5
        self.Image_Index = 0
        self.image = self.Images[self.Image_Index]
        self.List_Bullets = []
        self.Reloaded = False
        self.rect = self.image.get_rect(topleft=(self.Initial_x, self.Initial_y))
        self.Last_Shot_Time = 0

    def Player_Keys(self):
        Pressed_Key = pygame.key.get_pressed()
        if Pressed_Key[pygame.K_LEFT]:
            if self.rect.left <= 0:
                pass
            else:
                self.rect.move_ip(-3, 0)
            
        if Pressed_Key[pygame.K_RIGHT]:
            if self.rect.right > 800:
                pass
            else:
                self.rect.move_ip(3, 0)

        if Pressed_Key[pygame.K_UP]:
            if self.rect.top <= 0:
                pass
            else:
                self.rect.move_ip(0, -3)

        if Pressed_Key[pygame.K_DOWN]:
            if self.rect.bottom > 530:
                pass            
            else:
                self.rect.move_ip(0, 3)

        if Pressed_Key[pygame.K_SPACE]:
            if self.Reloaded:
                self.Last_Shot_Time = time.time()
                xy = (self.rect.topright[0]-5.005, self.rect.topright[1])
                Bullet_Rect = player.image.get_rect(midtop=(xy))
                self.List_Bullets.append(Bullet_Rect)
                self.Reloaded = False
            else:
                if time.time() - self.Last_Shot_Time > Bullet_Reload_Time: 
                    self.Reloaded = True
                else:
                    pass

    def update(self, speed):
        self.Image_Index += speed
 
        if self.Image_Index >= len(self.Images):
            self.Image_Index = 0
        
        self.image = self.Images[int(self.Image_Index)]

class Asteroid():

    def __init__(self, xy, index):
        self.x = xy[0]
        self.y = xy[1]
        self.List_Asteroids = [[100, Big_Asteroid_1]]
        self.Index = index
        self.Health = self.List_Asteroids[self.Index][0]
        self.Image = self.List_Asteroids[self.Index][1]   
        self.Rect = self.Image.get_rect(center=(self.x, self.y))

    def Destroyed(self, other):
        for i in range(1, 3):
            Images = pygame.image.load(f"assets/small_{i}_asteroid_{self.Index+1}.png")
            Rect = Images.get_rect(center=(self.x, self.y))
            xvel = 0
            yvel = 0
            if i == 1:
                yvel = 2
            elif i == 2:
                yvel = -2
                xvel = 1
            elif i == 3:
                xvel = -2
            List_Of_Small_Asteroids.append(other(Rect , Images, xvel, yvel))

    def __del__(self):
        print("asteroid Destroyed")

class Small_Asteroid():
    def __init__(self, rect, image, xvel, yvel):
        self.Rect = rect
        self.Image = image
        self.Health = 30
        self.xvel = xvel
        self.yvel = yvel

player = Player(INITIAL_PLAYER_X, INITIAL_PLAYER_Y)
player_group = pygame.sprite.Group(player)

def draw():
    for bullet in player.List_Bullets:
        screen.blit(Bullet_Img, bullet)

    for asteroid in List_Of_Asteroids: 
        screen.blit(asteroid.Image, asteroid.Rect)

    for small_asteroid in List_Of_Small_Asteroids:
        screen.blit(small_asteroid.Image, small_asteroid.Rect)

    player_group.update(0.25)
    player_group.draw(screen)

List_Of_Asteroids.append(Asteroid([100, 100], 0))

def check_collision():
    asteroid_list = []
    small_asteroid_list = []

    try:
        for asteroid in List_Of_Asteroids:
            for bullet in player.List_Bullets:
                if asteroid.Rect.colliderect(bullet):
                    if asteroid.Health > 0:
                        asteroid.Health -= Bullet_Damage
                        del bullet

            if asteroid.Health <= 0:
                asteroid.Destroyed(Small_Asteroid)
                del asteroid

        for small_asteroid in List_Of_Small_Asteroids:
            for bullet in player.List_Bullets:
                if small_asteroid.Rect.colliderect(bullet):
                    if small_asteroid.Health > 0:
                        small_asteroid.Health -= Bullet_Damage
                        del bullet

            if small_asteroid.Health <= 0:
                del small_asteroid
    except:
        pass

def move():
    for bullet in player.List_Bullets:
        if bullet.topleft[1] <= 0:
            player.List_Bullets.remove(bullet)
        else:
            bullet.move_ip(0, -Bullet_Velocity)

    for small_asteroid in List_Of_Small_Asteroids:
        xy = small_asteroid.Rect.center
        if xy[0] < -30:
            del small_asteroid
        if xy[0] > SCREEN_WIDTH+40:
            del small_asteroid
        if xy[1] < -30:
            del small_asteroid
        if xy[1] > SCREEN_HEIGHT+40:
            del small_asteroid
        else:
            small_asteroid.Rect.move_ip(small_asteroid.xvel, small_asteroid.yvel)

def main():
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        screen.fill(background_colour)
        player.Player_Keys()
        check_collision()
        move()
        draw()
        fpsClock.tick(FPS)
        pygame.display.update()

if __name__ == '__main__':
    main()```
  • 1
    You should use elif there, as if one of the first three if statements is fullfilled, the else clause is still executed, but because you just deleted `small_asteriod`, it raises an error. – The_spider Feb 09 '22 at 14:32
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community Feb 18 '22 at 23:54

0 Answers0