zodcham
zodcham

Reputation: 23

Pygame UnboundLocalError

The following code from a game engine I'm tinkering in gives me this error:

***"UnboundLocalError: local variable 'event' referenced before assignment":***

player.update(current_level.object_list, event) found in the main_loop() in line 342

main_loop() in line 364.

The only thing I try to do here is to transition from the red intro screen to the main loop by pressing return.

The engine itself ran fine until I made the game_intro() function and positioned it before the main_loop() function to run at the last couple of lines. I tried to play around with the 2 functions themselves to see what could make it work but no luck. Also placing event = None in line 340 above the player.update statement in line 339 allows me to transition from the intro to the main loop but the movement controls stop working.

I am pretty sure the error has to do something with the update() function in the Player class, but have no clue what causes it, especially when the intro function doesn't use event that's used in the update function (apart from pygame.event) and it only started crashing after adding the intro screen.

from pygame import *
import pygame
# from colours import *
# from textObjects import small, medium, large

black = pygame.Color(0, 0, 0)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
blue = pygame.Color(0, 0, 255)

pygame.font.init()
small = pygame.font.SysFont(None, 25)
medium = pygame.font.SysFont(None, 50)
large = pygame.font.SysFont(None, 80)

class Player(pygame.sprite.Sprite):
# Initialise function
    def __init__(self, color=blue, width=32, height=48, health=100):
        # I assume super() inherits everything from the block class
        super(Player, self).__init__()
        # Set the image to a Surface of size width and height
        self.image = pygame.Surface((width, height))
        # Fill the image with the default color of blue
        self.image.fill(color)
        # Use the Surface of the image to get the rectangular co-ordinates
        self.set_properties()
        # Create speed for x and y
        self.speed_x = 0
        self.speed_y = 0
        # Create health
        self.health = 100
        self.level = None

    def set_properties(self):
        self.rect = self.image.get_rect()
        # Create an x and y origin position (Centered the mouse on the sprite)
        self.origin_x = self.rect.centerx
        self.origin_y = self.rect.centery
        self.speed = 5
        # Create total travel distance to check the player's position on the map
        self.travel_distance_x = 0
        self.travel_distance_y = 0

    # Function to easily set the position of any block object on the center
    def set_position(self, x, y):
        self.rect.x = x - self.origin_x
        self.rect.y = y - self.origin_y

    # Function made to print the position on the map
    def print_position(self):
        self.travel_distance_x += self.speed_x
        self.travel_distance_y += self.speed_y
        print self.travel_distance_x, self.travel_distance_y

    def set_level(self, level):
        self.level = level
        self.set_position(level.player_start_x, level.player_start_y)

    def set_image(self, filename=None):
        if filename != None:
            self.image = pygame.image.load(filename).convert()
            self.set_properties()

    def death(self):
        print "Kill"
        return display_message("Vaziuojam", black)


    def update(self, collidable=pygame.sprite.Group(), event=True):
        self.experience_gravity()
        self.event = True
        self.rect.x += self.speed_x

        collision_list = pygame.sprite.spritecollide(self, collidable, False)
        for collided_object in collision_list:
            # Right direction
            if self.speed_x > 0:
                self.rect.right = collided_object.rect.left
            # Left direction
            elif self.speed_x < 0:
                self.rect.left = collided_object.rect.right
        self.rect.y += self.speed_y
        collision_list = pygame.sprite.spritecollide(self, collidable, False)
        for collided_object in collision_list:
            # Down direction
            if self.speed_y > 0:
                self.rect.bottom = collided_object.rect.top
                self.speed_y = 0
            # Up direction
            elif self.speed_y < 0:
                self.rect.top = collided_object.rect.bottom
                self.speed_y = 0
        if not event == None:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    self.speed_x = -self.speed
                    # self.change_speed(-self.speed, 0)
                if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    self.speed_x = self.speed
                    # self.change_speed(self.speed, 0)
                if event.key == pygame.K_UP or event.key == pygame.K_w:
                    if len(collision_list) >= 1:
                        self.speed_y = -(self.speed) * 2
                        # self.change_speed(0, -self.speed * 2)
                if event.key == pygame.K_DOWN:
                    # self.change_speed(0, self.speed)
                    pass
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    if self.speed_x < 0:
                        self.speed_x = 0
                if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    if self.speed_x > 0:
                        self.speed_x = 0

    def experience_gravity(self, gravity=0.35):
        if self.speed_y == 0:
            self.speed_y = 1
        else:
            self.speed_y += gravity


class Block(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, color=blue):
        # I assume super() inherits everything from the block class
        super(Block, self).__init__()
        # Set the image to a Surface of size width and height
        self.image = pygame.Surface((width, height))
        # Fill the image with the default color of blue
        self.image.fill(color)
        # Get rectangle object of the block
        self.rect = self.image.get_rect()
        # Assign x and y co-ordinates of the block
        self.rect.x = x
        self.rect.y = y

    def experience_gravity(self, gravity=0.35):
        if self.speed_y == 0:
            self.speed_y = 1
        else:
            self.speed_y += gravity


class Level(object):
    def __init__(self, player_object):
        self.object_list = pygame.sprite.Group()
        self.player_object = player_object
        self.player_start = self.player_start_x, self.player_start_y = 80, 150

        self.world_shift_x = 0
        self.world_shift_y = 0

        self.left_viewbox = screen_width / 2 - screen_width / 8
        self.right_viewbox = screen_width / 2 + screen_width / 8
        self.up_viewbox = screen_height / 3
        self.down_viewbox = screen_height / 2 # + screen_height / 12

    def update(self):
        self.object_list.update()

    def draw(self, screen):
        screen.fill(white)
        self.object_list.draw(screen)

    def shift_world(self, shift_x, shift_y):
        self.world_shift_x += shift_x
        self.world_shift_y += shift_y

        for each_object in self.object_list:
            each_object.rect.x += shift_x
            each_object.rect.y += shift_y

    def scroll(self):
        if self.player_object.rect.x <= self.left_viewbox:
            view_difference = self.left_viewbox - self.player_object.rect.x
            self.player_object.rect.x = self.left_viewbox
            self.shift_world(view_difference, 0)

        if self.player_object.rect.x >= self.right_viewbox:
            view_difference = self.right_viewbox - self.player_object.rect.x
            self.player_object.rect.x = self.right_viewbox
            self.shift_world(view_difference, 0)

        if self.player_object.rect.y <= self.up_viewbox:
            view_difference = self.up_viewbox - self.player_object.rect.y
            self.player_object.rect.y = self.up_viewbox
            self.shift_world(0, view_difference)

        if self.player_object.rect.y >= self.down_viewbox:
            view_difference = self.down_viewbox - self.player_object.rect.y
            self.player_object.rect.y = self.down_viewbox
            self.shift_world(0, view_difference)


class Level_01(Level):
    def __init__(self, player_object):
        super(Level_01, self).__init__(player_object)
        level = [
            #[x, y, width, height, color]
            [0, 3, 10, 844, black],
            [108, 0, 21, 730, black],
            [5, 838, 325, 9, black],
            [240, 815, 130, 32, black],
            [316, 782, 204, 64, black],
            [364, 749, 179, 96, black],
            [469, 680, 84, 156, black],
            [365, 805, 189, 42, black],
            [410, 715, 68, 56, black],
            [645, 679, 244, 18, black],
            [977, 678, 265, 13, black],
            [1439, 676, 93, 14, black],
            [1668, 670, 222, 16, black],
            [2068, 664, 359, 18, black],
            [2544, 617, 11, 64, black],
            [2653, 556, 11, 80, black],
            [2771, 484, 15, 113, black],
            [2922, 434, 277, 12, black],
            [2777, 327, 138, 15, black],
            [2659, 242, 20, 126, black],
            [2505, 178, 17, 145, black],
            [2226, 257, 176, 14, black],
            [2120, 266, 10, 92, black],
            [1808, 252, 213, 10, black],
            [1631, 265, 8, 86, black],
            [1231, 255, 293, 14, black],
            [1009, 261, 169, 12, black],
            [670, 259, 189, 18, black],
            [116, 127, 420, 20, black],
            [590, 183, 19, 95, black]
        ]
        for block in level:
            block = Block(block[0], block[1], block[2], block[3], block[4])
            self.object_list.add(block)

def set_message(text):
    global message, previous_message
    message = font.render(text, True, black, white)
    previous_message = message

def text_objects(text, color, size):
    if size == 'small':
        textSurface = small.render(text, True, color)
    if size == 'medium':
        textSurface = medium.render(text, True, color)
    if size == 'large':
        textSurface = large.render(text, True, color)
    return textSurface, textSurface.get_rect()

def display_message(text, color, y_displacement=0, size='small'):
    textSurface, textRectangle = text_objects(text, color, size)
    textRectangle.center = (screen_width / 2), (screen_height / 2) + y_displacement
    screen.blit(textSurface, textRectangle)

def character_message(text, color, y_displacement=0, size='small'):
    textSurface, textRectangle = text_objects(text, color, size)
    textRectangle = player.travel_distance_x , player.travel_distance_y + y_displacement
    screen.blit(textSurface, textRectangle)

# Initialise pygame module
pygame.init()

# Initialise pygame font
pygame.font.init()

# Defining the screen size
screen_size = screen_width, screen_height = 800, 600

# Setting the display and getting the Surface object
screen = pygame.display.set_mode(screen_size)

# Getting the Clock object
clock = pygame.time.Clock()

# Setting a title to the window
pygame.display.set_caption("TODO make title")

# Defining variable for FPS
fps_limit = 60

# Clear the screen
screen.fill(white)

# Setting the FPS at which the game will run
clock.tick(fps_limit)

# Group all the currently active objects
active_object_list = pygame.sprite.Group()

# Set variable player to the Player() class
player = Player()

# Add player to the active object list
active_object_list.add(player)

# Make a list for the levels and append Level_01 to that list with player as the handler
level_list = []
level_list.append(Level_01(player))

current_level_number = 0
current_level = level_list[current_level_number]

player.set_level(current_level)

# Define our font
font = pygame.font.SysFont(None, 25)
# Define a message, we're doing this because it will be used as a surface
message = previous_message = None
set_message("")


def game_intro():
    intro = True
    while intro:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    run=True
                    intro=False
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    quit()
        screen.fill(red)
        pygame.display.update()

def main_loop():
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
                run = False

        # Update functions
        player.update(current_level.object_list, event)
        event = None
        current_level.update()

        # Logic testing
        current_level.scroll()

        # Draw everything
        current_level.draw(screen)
        active_object_list.draw(screen)
        if player.travel_distance_y > 900:
            player.health = 0
            display_message("Death test message", black)

        # Delay fps
        clock.tick(fps_limit)

        # Update screen
        pygame.display.update()
        player.print_position()

game_intro()
main_loop()

I'm still trying only starting to comprehend classes and scopes so any tips would be a huge help, thank you in advance!

Upvotes: 0

Views: 78

Answers (1)

Peter
Peter

Reputation: 3495

As mentioned by molbdnilo, the indentation was wrong in the main loop. However, once you pass in the event, it's fine, but if there is no event, the player doesn't update.

The better way around this would be to make a global variable for events, then run the update function once per frame, and inside that check for events, but as to a quick fix, here you go.

def main_loop():
    run = True
    while run:
        current_events = pygame.event.get()
        if current_events:
            for event in current_events:
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                    run = False

                # Update functions
                player.update(current_level.object_list, event)
                current_level.update()

        else:
            player.update(current_level.object_list, None)
            current_level.update()

        # Logic testing
        current_level.scroll()

        # Draw everything
        current_level.draw(screen)
        active_object_list.draw(screen)
        if player.travel_distance_y > 900:
            player.health = 0
            display_message("Death test message", black)

        # Delay fps
        clock.tick(fps_limit)

        # Update screen
        pygame.display.update()
        player.print_position()

Upvotes: 1

Related Questions