Home > Blockchain >  Making a tower defence game but keep running into the error of local variable tower referenced befor
Making a tower defence game but keep running into the error of local variable tower referenced befor

Time:09-14

I have many different classes including Main, Enemy and Tower, in these classes i have methods and such, and i am trying to run a for loop provided by someone else on stack overlow (Thank you so much if your reading this), and i keep getting the same error local variable tower refferenced before assignment and i dont understand it.

import sys
import pygame
from pygame.locals import *
from random import *


WIDTH = 800
HEIGHT = 600

RED = (150, 0, 0)
LRED = (255, 0, 0)
GREEN = (0, 150, 0)
LGREEN = (0, 255, 0)
BLUE = (0, 0, 150)
LBLUE = (0, 0, 255)
CYAN = (0, 225, 230)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PURPLE = (150, 0, 150)
LPURPLE = (255, 0, 255)
COLORS = [RED, LRED, GREEN, LGREEN, BLUE, LBLUE, WHITE, PURPLE, LPURPLE]

pygame.init()
Mainclock = pygame.time.Clock()

light_image_map1 = pygame.image.load('Kami_lookout.png')
light_image_map1 = pygame.transform.scale(light_image_map1, (840, 630))
action_box_image = pygame.image.load('goku.png')
background_rectangle = pygame.Rect(0, 0, WIDTH, HEIGHT)
fantower_image = pygame.image.load('saibaman1.png')
fantower_image = pygame.transform.scale(fantower_image, (30, 40))
tower1 = pygame.image.load('goku.png')




def wave(quantity, size, distance):
    global saiba
    hh = True
    for i in range(quantity):
        saiba = Enemy(800   (distance   size)*i, 100- size/2, size, size)
        MainWindow.enemy.append(saiba)
    
   


        

def text_objects(text, font):
    textSurface = font.render(text, True, WHITE)
    return textSurface, textSurface.get_rect()

def button_text(msg, x, y, width, height, mouselse, mouseover, action = None, Text = True):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()

    if x width > mouse[0] > x and y height > mouse[1] > y:
        pygame.draw.rect(MainWindow.Gamewindow, mouseover,(x,y,width,height))
        if click[0] == 1 and action != None:
            action()
        else:
            pygame.draw.rect(MainWindow.Gamewindow, mouselse,(x,y,width,height))

    smallText = pygame.font.Font("freesansbold.ttf", 20)
    textSurf, textRect = text_objects(msg, smallText)
    textRect.center = ((x (width/2)), (y (height/2)))
    MainWindow.Gamewindow.blit(textSurf, textRect)

def button_tower(x, y, width, height, mouse, click, image, action = None):
    if x width > mouse[0] > x and y height > mouse[1] > y:
        if click[0] == 1 and action != None:
            MainWindow.action_box = action

def tower(width=30, height=30):
    global goku
    goku = Tower(MainWindow.mouse[0], MainWindow.mouse[1], width, height)
    MainWindow.tower.append(goku)
    

class Main:


    

    def __init__(self, width = WIDTH 100, height = HEIGHT   100):
        pygame.display.set_caption('DBZ Tower Defense')
        self.startwave = False
        self.width = width
        self.height = height
        self.Gamewindow = pygame.display.set_mode((self.width, self.height))

    def wave(self):
        self.startwave = True

    def Intro(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            self.Gamewindow.fill(BLACK)
            largeText = pygame.font.Font('freesansbold.ttf', 30)
            TextSurf, TextRect = text_objects("simple tower defense game", largeText)
            TextRect = (100, 100)
            self.Gamewindow.blit(TextSurf, TextRect)


            button_text("New game", 100, 200, 400, 50, GREEN, LGREEN, MainWindow.MainLoop)
            button_text("Continue", 100, 300, 400, 50, RED, LRED)
            button_text("Exit", 100, 400, 400, 50, BLUE, LBLUE, quit)
            pygame.display.update()

    

    def MainLoop(self):
        self.enemy = []
        self.tower = []

        

        self.action_box = None
        while True:
            Mainclock.tick(60)

            self.mouse = pygame.mouse.get_pos()
            self.click = pygame.mouse.get_pressed()
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
                if event.type == pygame.KEYDOWN:
                    if event.key == ord('t'):
                        tower()
                
            if self.startwave == True and len(self.enemy)==0:
                wave(10, 20, 8)
                self.startwave = False
            
            
            
            
            
            

            for index,ene in enumerate(self.enemy):
                
                ene.update()
                ene.move()
                if ene.rect.left <= 0:
                    del self.enemy[index]
            
            

            self.Gamewindow.fill(CYAN)
            self.Gamewindow.blit(light_image_map1, background_rectangle)
            button_tower(800, 0, 50, 50, self.mouse, self.click, fantower_image, tower)

            if pygame.mouse.get_pressed()[0] == 1 and self.action_box != None:
                rectangle30 = pygame.Rect(self.mouse[0]-15, self.mouse[1]-15, 30, 30)
                self.Gamewindow.blit(action_box_image, rectangle30)
            elif self.action_box != None:
                self.action_box()
                self.action_box = None
            for object_enemy in self.enemy:
                self.Gamewindow.blit(fantower_image, object_enemy.rect)
            for object_tower in self.tower:
                self.Gamewindow.blit(object_tower.image, object_tower.rect)

            button_text("Start next wave", 0, 600, WIDTH, 100, PURPLE, LPURPLE, MainWindow.wave)
            

      **#This is where the problem is**




            **for tower in self.tower:
                tower_rect = tower.get_rect()
                for enemy in self.enemy:
                    if ( enemy.collidesWith( tower_rect ) ):
                        # Make enemy dead
                        enemy.die()**



       **#There might be a problem here in future too**



            
            **for i in range( len( self.enemy ) - 1, -1, -1):   # note: loop backwards
                if ( self.enemy[i].isDead() ):
                    del( self.enemy[i] )**












            pygame.display.update()

class Enemy:
    def __init__(self, x, y, width, height):
        self.dead = False
        self.rect = pygame.Rect(x, y, width, height)
        self.dir = 4
        self.movement = [(810, 100, 2), (810, 300, 4), (575, 300, 8), (575, 230, 4), (385, 230, 2), (385, 300, 4), (276, 300, 2), (276, 392, 4), (70, 392, 8), (70, 300, 4)]

    def move(self): 
        if self.dir == 8:
            self.rect.centery -= 1
        if self.dir == 4:
            self.rect.centerx -= 1
        if self.dir == 6:
            self.rect.centerx  = 1
        if self.dir == 2:
            self.rect.centery  = 1
    
    def update(self):
        for pos in self.movement:
            if self.rect.center == (pos[0], pos[1]):
                self.dir = pos[2]
    
    def color(self, colorid):
        return COLORS[colorid]

    def die( self, action=True ):
        self.dead = action

    def isDead( self ):
        return self.dead

    def collidesWith( self, other_rect ):
        """ Return true, if other_rect overlaps my rect """
        result = self.rect.colliderect( other_rect )

    def get_rect( self ):
        """ Get a copy of the rect, in PyGame style """
        return self.rect.copy()
        
    
    




class Tower:
    def __init__(self, x, y, width, height):
        self.rect = pygame.Rect(x-15, y-15, width, height)
        self.image = pygame.transform.scale(tower1, (50, 50))

        def colliderec(self):
            if self.rect.collidedict(self.rect):
                print("true")
    
#if Enemy.rect.collideRect(Tower.rect):
 #   print("true")


class GameInformation:
    def __init__(self, money, health, x, y):
        self.screen_X = x
        self.screen_Y = y
        self.money = money
        self.health = health

    def display_text(win, text, x, y):
        pygame.font.init()
        myfont = pygame.font.SysFont('Comic Sans MS', 30)
        textsurface = myfont.render(text, False, (0, 0, 0))
        win.blit(textsurface, (x, y))

    
class Collide:
    def __init__(self, pos_x, pos_y, size_x, size_y):
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.size_x = size_x
        self.size_y = size_y
        self.hitbox_rect = Rect(pos_x, pos_y, size_x, size_y) # rectangle hitbox



MainWindow = Main()
MainWindow.Intro()

CodePudding user response:

There's some issues referencing which variable is from which object. And I think it's just a question-paste issue, but it's not clear if wave() belongs as a member of class Main or not. I assumed it was, and added some selfs where needed.

I hate the whole "idiomatic code" paradigm, but generally PyGame "sprite like" objects define an image and a rectangle. Most examples do it like this, and the Sprite Class uses it too. It's a simple but powerful method. The rectangle defines the position and bounds of the sprite, and the image what it looks like. I did modify the Tower and Enemy classes to use this idiom.

The code was mostly OK already. You need to be careful with referencing things inside the local class, Vs other classes. Inside the class for local members, it's always self.member_name. And don't forget the self as the first parameter.

Anyway I played with the code to the point where it works. You may want to compare this code with your own, with a comparison tool like Meld. There was nothing in particular that was broken, just a handful of little issues. I had to comment out some stuff that I didn't have access to, like the buttons, and substitute images.

import sys
import pygame

WIDTH = 800
HEIGHT= 800

CYAN=(0x00, 0xff, 0xff)

pygame.init()
Mainclock = pygame.time.Clock()

class Tower:
    def __init__(self, x, y, width, height):
        self.image = pygame.image.load( "tower2.png" ).convert_alpha()
        self.image = pygame.transform.smoothscale( self.image, ( width, height ) )
        self.rect = self.image.get_rect()
        self.rect.center = ( x, y )

    def get_rect( self ):
        """ Get a copy of the rect, in PyGame style """
        return self.rect.copy()


        
class Enemy:
    def __init__(self, x, y, width, height):
        self.dead = False
        self.dir = 4
        self.movement = [(810, 100, 2), (810, 300, 4), (575, 300, 8), (575, 230, 4), (385, 230, 2), (385, 300, 4), (276, 300, 2), (276, 392, 4), (70, 392, 8), (70, 300, 4)]
        self.image = pygame.image.load( "monster.png" ).convert_alpha()
        self.image = pygame.transform.smoothscale( self.image, ( width, height ) )
        self.rect = self.image.get_rect()
        self.rect.topleft = ( x, y )

    def move(self): 
        if self.dir == 8:
            self.rect.centery -= 1
        if self.dir == 4:
            self.rect.centerx -= 1
        if self.dir == 6:
            self.rect.centerx  = 1
        if self.dir == 2:
            self.rect.centery  = 1

    def update(self):
        for pos in self.movement:
            if self.rect.center == (pos[0], pos[1]):
                self.dir = pos[2]

    def color(self, colorid):
        return COLORS[colorid]

    def die( self, action=True ):
        self.dead = action

    def isDead( self ):
        return self.dead

    def collidesWith( self, other_rect ):
        """ Return true, if other_rect overlaps my rect """
        return self.rect.colliderect( other_rect )

    def get_rect( self ):
        """ Get a copy of the rect, in PyGame style """
        return self.rect.copy()

    
class Main:

    def __init__(self, width = WIDTH 100, height = HEIGHT   100):
        pygame.display.set_caption('DBZ Tower Defense')
        self.startwave = False
        self.width = width
        self.height = height
        self.Gamewindow = pygame.display.set_mode((self.width, self.height))
        # Load images
        self.light_image_map1 = pygame.image.load( "background.png" ).convert_alpha() 
        self.light_image_map1 = pygame.transform.smoothscale( self.light_image_map1, ( width, height ) )
        self.background_rectangle = self.light_image_map1.get_rect()
        self.background_rectangle.topleft = (0,0)


    def wave(self):
        self.startwave = True

    def Intro(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()

            self.Gamewindow.fill(BLACK)
            largeText = pygame.font.Font('freesansbold.ttf', 30)
            TextSurf, TextRect = text_objects("simple tower defense game", largeText)
            TextRect = (100, 100)
            self.Gamewindow.blit(TextSurf, TextRect)

            button_text("New game", 100, 200, 400, 50, GREEN, LGREEN, MainWindow.MainLoop)
            button_text("Continue", 100, 300, 400, 50, RED, LRED)
            button_text("Exit", 100, 400, 400, 50, BLUE, LBLUE, quit)
            pygame.display.update()


    def MainLoop(self):
        self.enemy = []
        self.tower = []
        self.action_box = None

        self.startwave = True   # Don't have button code, force start

        while True:
            Mainclock.tick(60)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
                elif ( event.type == pygame.MOUSEBUTTONUP ):
                    # create a tower where the mouse was clicked
                    mouse = pygame.mouse.get_pos()
                    click = pygame.mouse.get_pressed()
                    self.tower.append( Tower( mouse[0], mouse[1], 64, 64 ) )
                
            if self.startwave == True and len(self.enemy)==0:
                self.wave(10, 20, 8) 
                self.startwave = False
            
            for i in range( len( self.enemy ) - 1, -1, -1):   # note: loop backwards
                self.enemy[i].update()
                self.enemy[i].move()
                if ( self.enemy[i].rect.left <= 0 ):
                    del( self.enemy[i] )
            
            self.Gamewindow.fill(CYAN)
            self.Gamewindow.blit(self.light_image_map1, self.background_rectangle)
            #button_tower(800, 0, 50, 50, self.mouse, self.click, fantower_image, tower)

            if pygame.mouse.get_pressed()[0] == 1 and self.action_box != None:
                rectangle30 = pygame.Rect(self.mouse[0]-15, self.mouse[1]-15, 30, 30)
                self.Gamewindow.blit(action_box_image, rectangle30)
            elif self.action_box != None:
                self.action_box()
                self.action_box = None

            for object_enemy in self.enemy:
                self.Gamewindow.blit(object_enemy.image, object_enemy.rect)
            for object_tower in self.tower:
                self.Gamewindow.blit(object_tower.image, object_tower.rect)

            #button_text("Start next wave", 0, 600, WIDTH, 100, PURPLE, LPURPLE, MainWindow.wave)
            

            for tower in self.tower:
                for enemy in self.enemy:
                    if ( enemy.collidesWith( tower.get_rect() ) ):
                        # Make enemy dead
                        print("COLLIDES WITH TOWER")
                        enemy.die()
            
            for i in range( len( self.enemy ) - 1, -1, -1):   # note: loop backwards
                if ( self.enemy[i].isDead() ):
                    del( self.enemy[i] )

            pygame.display.update()

    def wave( self, quantity, size, distance):    # <<-- Made member function of MainWindow
        global saiba
        hh = True
        for i in range(quantity):
            saiba = Enemy(800   (distance   size)*i, 100- size/2, size, size)
            self.enemy.append(saiba)


# MAIN
MainWindow = Main()
MainWindow.MainLoop()

CodePudding user response:

I think when I see these types of errors it usually means that I'm trying to access an object that hasn't been declared yet. I'm sure you know what constructors are, therefore did you initialize a variable before getting into your main() loops?

Example:

# step 1: Declare class
class Dog:
  pass

# step 2: Initialize a new Dog  
my_dog = Dog() # Did you skip this?
my_dog.name = "scruffy"

# step 3: Driver code that gets work done
print(my_dog.name) # you are probably here and forgot step 2
  • Related