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 self
s 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 rect
angle 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