EvaYohane
EvaYohane

Reputation: 33

Having trouble with a "NameError"

I'm new to classes, and this is my third attempt at making one. I've ran into a NameError which I really have no idea how to solve. Take a look at my program and see if you can help.

import random
import math
import pygame
import pickle 

# initialise pygame
pygame.init()

# player class 
class player(object):
    def __init__(self, playerimage, playerX, playerY = 700, playerX_change = 0):
        self.playerimage = pygame.image.load("Main Player.png") 
        self.playerX = 365
        self.playerY = 700 
        self.playerX_change = 0 

# laser class 
# ready - bullet not on screen
# fire - bullet is shown on screen and is moving

class laser(object):
    def __init__(self, laserimage, laserX, laserY, laserY_change):
        self.laserimage = pygame.image.load("laser.png")
        self.laserX = 0
        self.laserY = 700 
        self.laserY_change = 10
        self.laser_state = "ready"

# alien player / random movement = random.randint()
class alien(object):
    def __init__(self, alienimage, alienX, alienY, alienX_change, alienY_change, amount_aliens):
        self.alienimage = pygame.image.load("alien.png")
        self.alienX = []
        self.alienY = []
        self.alienX_change = []
        self.alienY_change = []
        self.amount_aliens = 10

    for i in range(ufo.amount_aliens):
        ufo.alienimage.append(pygame.image.load('alien.png'))
        ufo.alienX.append(random.randint(0, 735))
        ufo.alienY.append(random.randint(50, 200))
        ufo.alienX_change.append(3)
        ufo.alienY_change.append(7)

score = 0


# define player
def main_player(x, y):
    screen.blit(male.playerimage, (x, y))


# define laster
def fire_laser(x, y):
    lasr.laser_state = "fire"
    screen.blit(lasr.laserimage, (x + 16, y + 10))


# define alien
def alien(x, y, i):
    screen.blit(ufo.alienimage[i], (x, y))


# collision detection
def hascollision(alienX, alienY, laserX, laserY):
    distance = math.sqrt((math.pow(alienX - laserX, 2)) + (math.pow(alienY - laserY, 2)))
    if distance < 27:
        return True
    else:
        return False

#frames per second 
clock = pygame.time.Clock()

# background
background = pygame.image.load('stars.png')

# display and screen title/icon
(width, height) = (800, 800)
screen = pygame.display.set_mode((width, height))
flip = pygame.display.flip()
pygame.display.set_caption("space fighters")
pygame.event.get()
icon = pygame.image.load('logo.png')
pygame.display.set_icon(icon)

from sys import exit

ufo = alien()
lasr = laser(0, 700, 32, 32)
male = player(365, 700,)
# loop of functions
executed = True
while executed:
    screen.fill((63, 62, 63))
    # image background
    screen.blit(background, (0, 0))
    for event in pygame.event.get():
        # if key pressed, check which input, right or left?
        if event.type == pygame.KEYDOWN:
            print("key pressed")
            if event.key == pygame.K_a:
                male.playerX_change = -6
            if event.key == pygame.K_s:
                male.playerX_change = 6
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_a or event.key == pygame.K_s:
                male.playerX_change = 0
            if event.key == pygame.K_SPACE:
                if lasr.laser_state is "ready":
                    lasr.laserX = male.playerX
                fire_laser(lasr.laserX, lasr.laserY)
    #frames per second is 60fps
    clock.tick(60)
    # bounrary algorithm, prevents player moving out/enemy.
    male.playerX += male.playerX_change

    if male.playerX <= 0:
        male.playerX = 0
    elif male.playerX >= 736:
        male.playerX = 736

    # boundry algorithm, make sure alien doesn't go out of bountry
    for i in range(ufo.amount_aliens):
        ufo.alienX[i] += ufo.alienX_change[i]
        if ufo.alienX[i] <= 0:
            ufo.alienX_change[i] = 4
            ufo.alienY[i] += ufo.alienY_change[i]
        elif ufo.alienX[i] >= 736:
            ufo.alienX_change[i] = -4
            ufo.alienY[i] += ufo.alienY_change[i]

    # collision
        collision = hascollision(ufo.alienX[i], ufo.alienY[i], lasr.laserX, lasr.laserY)
        if collision:
            lasr.laserY = 650
            lasr.laser_state = "ready"
            score += 5
            print(score)
            alienX[i] = random.randint(0, 735)
            alienY[i] = random.randint(50, 200)

        alien(ufo.alienX[i], ufo.alienY[i], i)

    # movement of laser shot
    if lasr.laserY <= 0:
        lasr.laserY = 650
        lasr.laser_state = "ready"

    if lasr.laser_state is "fire":
        fire_laser(lasr.laserX, lasr.laserY)
        lasr.laserY -= lasr.laserY_change

    # updates screen to show screen
    main_player(male.playerX, male.playerY)
    pygame.display.update()
pygame.quit()

This is the output of the error given by visual studio code (it is on line 39)
for i in range(ufo.amount_aliens): NameError: name 'ufo' is not defined

Upvotes: 3

Views: 165

Answers (1)

Kingsley
Kingsley

Reputation: 14906

The alien class is a bit mixed up. (Although it's hard to tell if this is just an indentation issue in the SO paste.) I'm going to assume that the list of ufos needs to be made outside the class, because this is the only thing that makes sense. Later on in the code, you declare an alien function which will occlude the alien class too. You will need to fix this first - it's best moved into the alien class as alien.draw()

So to make a bunch of aliens, create a list:

alien_image = pygame.image.load('alien.png')
all_ufos = []
for i in range( amount_aliens ):
    x_pos = random.randint(  0, 735 )
    y_pos = random.randint( 50, 200 )
    x_speed = 3
    y_speed = 7
    all_ufos.append( alien( alien_image, x_pos, y_pos, x_speed, y_speed ) )

Remove the amount_aliens from the alien object, so that it now only represents a single alien.

class alien( object ):
    def __init__( self, alienimage, alienX, alienY, alienX_change, alienY_change ):
        self.alienimage = alienimage
        self.alienX = alienX
        self.alienY = alienY
        self.alienX_change = alienX_change
        self.alienY_change = alienY_change

And move the support functions into the alien class.

   def draw( self, screen ):
       """ Draw the alien to the screen """
       screen.blit( self.alienimage, ( self.alienX, self.alienY ) )

   def hasCollision( self, laserX, laserY ):
       """ Has the laser at collided with this alien? """
       distance = math.sqrt((math.pow(self.alienX - laserX, 2)) + (math.pow(self.alienY - laserY, 2)))
       return ( distance < 27 ):

This allows your main loop to iterate over the list of aliens, doing stuff simply:

### Main Loop
while not exiting:
    ...

    # paint all UFO sprites
    for ufo in all_ufos:
        ufo.draw( screen )

    # check all lasers for collision
    for laser in all_lasers:
        for ufo in all_ufos:
            if ( ufo.hasCollision( laser.laserX, laser.laserY ) ):
                print( "*boom*" )

What you are doing here is re-creating some of the functionality of the PyGame Sprite and SpriteGroup Classes. It might be worth a quick read of the documentation on it.

Upvotes: 3

Related Questions