简体   繁体   中英

Blit object from a Class into a Function (Pygame)

some of you may have seen my previous questions regarding a Pygame project I'm currently working on, but I decided to do a rewrite and follow a proper object oriented programming since it wasn't really working out.

This is what I have so far:

###### Import & Init ######
import pygame
import os, random, math, copy, sys

pygame.init()

###### Variables ######
displayWidth, displayHeight = 600, 600
shipWidth, shipHeight = 50, 50

# Variables that will be used to centre the ship.
startX = displayWidth / 2
startY = displayHeight - 40

screen = pygame.display.set_mode((displayWidth, displayHeight))
pygame.display.set_caption('Space Arcader')

####### Colours #######
# Colour list containing most common colours.
# Colour      R    G    B
red      = (255,   0,   0)
green    = (  0, 255,   0)
blue     = (  0,   0, 255)
grey     = (100, 100, 100)
black    = (  0,   0,   0)
white    = (255, 255, 255)
# Create a list from the colours in order to call it later.
colourList = [red, green, blue, black, white]

####### Classes #######
# Ship class
class Ship(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load("assets/ship.png").convert_alpha()
        self.image = pygame.transform.scale(self.image,(shipWidth, shipHeight))
        self.transform = self.image

        self.rect = self.image.get_rect()
        self.rect.centerx = startX
        self.rect.centery = startY

        # Where the arrow will be pointing on game start
        self.angle = 90

    def update(self, direction):
        if direction == 'right' and self.angle > 20:
            self.angle -= 4
        elif direction == 'left' and self.angle < 160:
            self.angle += 4

        self.transform = pygame.transform.rotate(self.image, self.angle)
        self.rect = self.transform.get_rect()
        self.rect.centerx = startX
        self.rect.centery = startY

    def draw(self):
        screen.blit(self.transform, self.rect)

# Score class
class Score(object):
    def __init__(self):
        self.total = 0
        self.font = pygame.font.SysFont('Helvetica', 15)
        self.render = self.font.render('Score: ' + str(self.total), True, white)
        self.rect = self.render.get_rect()
        self.rect.left = 5
        self.rect.bottom = displayHeight - 2
        self.render.set_colorkey((0,0,0))

    def update(self, delete_scoreList):
        self.total += ((len(delete_scoreList)) * 50)
        self.render = self.font.render('Score: ' + str(self.total), True, white)

    def draw(self):
        screen.blit(self.render, self.rect)

# Game class
class MainGame(object):
    def __init__(self):
        self.score = 0
        self.game_over = False      

    def controls(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                Menu.terminate()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    direction = 'left'
                elif event.key == pygame.K_RIGHT:
                    direction = 'right'
            elif event.type == pygame.KEYUP:
                direction = None
                if event.key == pygame.K_SPACE:
                    bullet = Bullet()
                    bullet.rect.x = arrow.rect.x
                    bullet.rect.y = arrow.rect.y
                    all_sprites_list.add(bullet)
                    bulletList.add(bullet)
                elif event.key == pygame.K_ESCAPE:
                    running = False
                    MenuInit()

    def displayInit(self, screen):
        # Set screen width and height.
        display = pygame.display.set_mode((displayWidth, displayHeight))

        # Set the background image of the window.
        background = pygame.image.load("assets/background.jpg")

        # Blit the background onto the screen.
        screen.blit(background, (0, 0))

        # Disable mouse visibility.
        pygame.mouse.set_visible(False)

        # Code to redraw changing/moving objects.
        pygame.display.flip()

# Menu class
class Menu:
    hovered = False
    def __init__(self, text, pos):
        self.text = text
        self.pos = pos
        self.set_rect()
        self.draw()

    def draw(self):
        self.set_rend()
        screen.blit(self.rend, self.rect)

    def set_rend(self):
        menu_font = pygame.font.SysFont('Helvetica', 40)
        self.rend = menu_font.render(self.text, True, self.get_color())

    def get_color(self):
        if self.hovered:
            return (white)
        else:
            return (grey)

    def set_rect(self):
        self.set_rend()
        self.rect = self.rend.get_rect()
        self.rect.topleft = self.pos

    def terminate():
        pygame.quit()
        sys.exit()

####### Functions #######
def MenuInit():
    # Set the background image of the window.
    background = pygame.image.load("assets/menuBackground.jpg")

    options = [Menu("Start game", (200, 250)), Menu("Quit", (265, 300))]

    # Enable mouse visibility.
    pygame.mouse.set_visible(True) 

    while True:       
        for option in options:
            if option.rect.collidepoint(pygame.mouse.get_pos()):
                option.hovered = True
            else:
                option.hovered = False
            option.draw()

        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                for option in options:
                    if option.hovered and option.text == "Start game":
                        MainInit()
                    elif option.hovered and option.text == "Quit":
                        Menu.terminate()

        pygame.display.update()
        screen.blit(background,(0,0))

def MainInit():
    # Manage the refresh rate
    clock = pygame.time.Clock()

    # Loop the game until the user closes the application.
    running = True

    # Open an instance of the Game class
    game = MainGame()
    ship = Ship()
    score = Score()

    # Main Loop.
    while running:
        draw = ship.draw()
        ship.update(direction)
#        ship.update(direction)
#        ship.draw()
        controls = game.controls()
        game.displayInit(screen)

        # Refresh rate speed (frames per second).
        clock.tick(60)

# Open the menuInit() function which brings up the main menu.  
if __name__ == '__main__':
    MenuInit()

So my problem is trying to blit the ship and score onto the MainInit() function which calls the game class object as you can see above. Calling the game class object works fine because the background image changes and the controls work perfectly. However, when I follow the same method for ship and score, it doesn't seem to work. In the commented out comments, you can see I tried a few things but I got various errors such as "NameError: global name 'direction' is not defined" or NameError: global name 'update' is not defined

Any pointers? :)

Thank you very much.

The problem is caused by an out-of-scope variable - exactly as the error is telling you: global name 'direction' is not defined" .

You use direction in your def MainInit() , but direction is never defined in that function. The place you define/set a direction -variable, is in class MainGame.controls() .

The problem is, however, that the direction -variable created in class MainGame.controls() is local only. It will only exist within that specific function, MainGame.controls() . When that function is not used any longer, the value of direction will cease to exist - which is why there is no such thing as direction defined in def MainInit() . It's out of scope .


To fix this problem, you can choose to use direction as a global variable. It requires you to define the direction value outside any functions, so at the very beginning should work.

Whenever you want to read/modify that specific global variable, you should use the global keyword, to tell your Python function that you want to use/modify a global variable, and not a local one

global direction

This might be of interest to you: https://stackoverflow.com/questions/423379/using-global-variables-in-a-function-other-than-the-one-that-created-them


Personally I would not use global variables, but rather store a direction member variable in the Ship -class and directly change that.

Global variables can become quite a mess.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM