【问题标题】:Python(pygame) class import scope troublesPython(pygame) 类导入作用域问题
【发布时间】:2016-04-27 08:53:32
【问题描述】:

您好,这是我在任何论坛上的第一篇文章,我通常只是浏览并自己解决挑战,但我的头因撞到墙上而变得相当疼痛。

我对编程很陌生,大约有 6 周的时间来玩我的名字。 我正在使用Linux,带有python3.5。

在尝试减小我的主模块的大小时遇到​​了麻烦,我一直在尝试将我的课程删除到不同的模块,并且我在某些方面取得了成功

ie:我可以将播放器类放入lib文件夹并在main中hash出播放器,它运行良好,直到我将播放器移动到洞穴或商店,这个阶段的游戏崩溃了。

我知道这是我从大量阅读中遇到的范围问题,我可以看到其他人也遇到过类似的问题,但我就是无法理解/理解解决方案。

我尝试在不同的位置使用 print(globals()) 来定位问题以查看是否存在差异,我认为这是由于第一次调用来自 if guard 并且在 this 中定义了播放器,但是player 没有像我在主脚本中有播放器类时那样定义为全局。

#!/usr/bin/env pyhton

import pygame
from Lib.Colours import *
import random
import os
#imports player class when its in different module
#from Lib.Player import *

# used for importing, when player class is in different module               
#from RPGv1_TryModule import *


class Player ( pygame.sprite.Sprite ):
    #player class blue print
    def __init__(self):

        super( Player, self ).__init__()
        self.image = pygame.image.load(os.path.join("Data/Images",'player1.png')).convert_alpha()
        self.image.get_rect()
        #gets players properites and stats
        self.set_properites()
        self.set_herostats()
        #set variable for H and V speed
        self.vertical_speed = 0
        self.horizontal_speed = 0
        #variables for area(map) and if in shop
        self.area = 0
        self.shop = 0

    def set_herostats(self):
        #players stats when first created
        self.lvl = 1
        self.hp = 100
        self.maxhp = 100
        self.att = [5, 10]
        self.defence = 1
        self.combatspeed = 1
        self.exp = 0
        self.maxexp = 100

    def set_properites(self):
        #gets size of player and sets center point
        self.rect = self.image.get_rect()
        self.origin_x = self.rect.centerx
        self.origin_y = self.rect.centery
        #sets movement speed
        self.speed = 2

    def set_position( self, x, y ):
        #tracks players position
        self.rect.x = x - self.origin_x
        self.rect.y = y - self.origin_y

    def set_area(self, area ):
        #start positon set by area
        self.area = area
        self.set_position(area.player_start_x, area.player_start_y)

    def update(self, collide = pygame.sprite.Group, interact = pygame.sprite.Group, event=None):
        #player movement in x axis
        self.rect.x += self.horizontal_speed
        collision_list = pygame.sprite.spritecollide(self, collide, False )
        # collision detaction and making blocks in passable in x axis
        for collided_object in collision_list:
            if ( self.horizontal_speed > 0 ):  
                self.rect.right = collided_object.rect.left

            elif ( self.horizontal_speed < 0 ):
                self.rect.left = collided_object.rect.right
        #player movement in Y axis
        self.rect.y += self.vertical_speed
        collision_list = pygame.sprite.spritecollide(self, collide, False )
        # collision detaction and making blocks in passable in Y axis
        for collided_object in collision_list:
            if ( self.vertical_speed > 0 ):
                self.vertical_speed = 0   
                self.rect.bottom = collided_object.rect.top

            elif ( self.vertical_speed < 0 ):
                self.vertical_speed = 0
                self.rect.top = collided_object.rect.bottom
        #interactive block collision detection
        interaction_list = pygame.sprite.spritecollide(self, interact, False )
        # interactive block events
        for interaction in interaction_list:
            print(interaction.name)
            self.vertical_speed = 0
            self.horizontal_speed = 0
            if interaction.name == 'cave1':
                change_area(1)
            if interaction.name == 'Town':
                change_area()
            if interaction.name == 'shops':
                shopping()

        # key commands such as movement keys
        if not ( event == None ):
            if ( event.type == pygame.KEYDOWN ):

                if ( event.key == pygame.K_UP ):
                    self.vertical_speed += -self.speed

                if ( event.key == pygame.K_DOWN ):
                    self.vertical_speed += self.speed

                if ( event.key == pygame.K_LEFT ):
                    self.horizontal_speed += -self.speed

                if ( event.key == pygame.K_RIGHT ):
                    self.horizontal_speed += self.speed

                #key up sets speed back to 0 = player stop moving
            if ( event.type == pygame.KEYUP ):

                if ( event.key == pygame.K_UP ):
                    self.vertical_speed = 0

                if ( event.key == pygame.K_DOWN ):
                    self.vertical_speed = 0

                if ( event.key == pygame.K_LEFT ):
                    self.horizontal_speed = 0

                if ( event.key == pygame.K_RIGHT ):
                    self.horizontal_speed = 0
        # random battle logic (currently in construction)
        #if current_area.areatype == 'Cave':
            #0.1% chance to spawn mob while moving
         #   chance = random.randint(1,1000)
          #  if chance in range(1,2):
           #     if self.horizontal_speed != 0 or self.vertical_speed != 0:
            #        print('SpawnMob')


class Block( pygame.sprite.Sprite ):
    #none passable objects blueprint
    def __init__( self, x, y, width, height, colour = black ):

        super( Block, self ).__init__()
        self.image = pygame.Surface( ( width, height ) )

        self.image.fill ( colour )

        self.rect = self.image.get_rect()

        self.rect.x = x 
        self.rect.y = y 

class Interactive_block( pygame.sprite.Sprite ):
    #interactive blocks blueprint( interactive event set in player class )
    def __init__( self, name, x, y, image ):
        super( Interactive_block, self ).__init__()
        self.image = pygame.image.load(os.path.join('Data/Images', image ) )
        self.name = name

        self.rect = self.image.get_rect()

        self.rect.x = x 
        self.rect.y = y 


class Area ( object ):

    def __init__(self, player_object):
        #adding to sprite groups
        self.block_list = pygame.sprite.Group()
        self.interactiveblock_list = pygame.sprite.Group()

        self.player_object = player_object

        self.player_start = self.player_start_x, self.player_start_y = \
                            0, 0
        #mob spawn variable
        self.areatype = None
        #world shift start variable
        self.world_shift_x = self.world_shift_y = 0
        #camera settings
        self.left_viewbox = display_width/2
        self.right_viewbox = display_width/2

        self.up_viewbox = display_height/2
        self.down_viewbox = display_height/2

    def shift_world(self, shift_x, shift_y ):
        #shifts blocks in area 
        self.world_shift_x += shift_x
        self.world_shift_y += shift_y

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

        for each_object1 in self.block_list:
                each_object1.rect.x += shift_x
                each_object1.rect.y += shift_y

    def run_viewbox(self):
        #basic camera to follow player
        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 )

    def update(self):
        #update function for areas
        window.fill(self.backcolour)

        self.block_list.update()
        self.interactiveblock_list.update()

    def draw(self, window ):
        #draw function for areas
        self.block_list.draw( window )
        self.interactiveblock_list.draw( window )

class Main_town(Area):
    #start area 
    def __init__(self, Player_object ):

        super( Main_town, self ).__init__(Player_object)

        self.backcolour = green

        boarder_colour = water

        self.player_start = self.player_start_x, self.player_start_y = \
                            752, 711
        # used for no mob spawn
        self.areatype = 'Town'
        #none passable objects
        boarders = [
                [0, 2, 153, 997, boarder_colour ],
                [12, 851, 1841, 148, boarder_colour ],
                [1669, 0, 185, 988, boarder_colour ],
                [0, 0, 1834, 119, boarder_colour ],
                [194, 144, 1443, 33, wall ],
                [1600, 177, 37, 633, wall ],
                [196, 768, 1404, 42, wall ],
                [196, 171, 39, 597, wall ],
                [504, 408, 29, 362, wall ],
                [669, 173, 32, 166, wall ],
                [898, 167, 29, 169, wall ],
                [1047, 414, 35, 358, wall ],
                [1256, 172, 27, 194, wall ],
                [1421, 568, 185, 30, wall ],
                [1287, 611, 23, 157, wall ],
                [1442, 175, 17, 189, wall ],
              ]

        for block in boarders:
            block = Block( block[0], block[1], block[2], block[3], block[4] )
            self.block_list.add( block )

        #list of rough coords for objects, created with basic map editor 
        #shops coords  (371, 688)
        #shops coords  (323, 248)
        #shops coords  (548, 249)
        #shops coords  (809, 260)
        #caves coords  (1364, 241)
        #caves coords  (1529, 241)
        #caves coords  (1503, 692)
        #caves coords  (1200, 683)

        #interactive objects
        interactive_block = [
              ['cave1',1114,236,'Cave.png'],
              ['shops',240,175,'building.png'],
              ]

        for block in interactive_block:
            block = Interactive_block( block[0], block[1], block[2], block[3] )
            self.interactiveblock_list.add( block )


class Cave1(Area):
    # first cave map ( created with basic map editor )
    def __init__(self, Player_object ):

        super( Cave1, self ).__init__(Player_object)

        self.backcolour = grey

        boarder_colour = black

        self.player_start = self.player_start_x, self.player_start_y = \
                            193, 795
        # used for mob spawns
        self.areatype = 'Cave'
        # None passable objects
        boarders = [
            [0, 1, 159, 998, boarder_colour ],
            [148, 848, 1705, 151, boarder_colour ],
            [1725, 0, 128, 849, boarder_colour ],
            [61, 0, 1665, 81, boarder_colour ],
            [256, 641, 45, 214, boarder_colour ],
            [261, 77, 41, 197, boarder_colour ],
            [257, 366, 51, 115, boarder_colour ],
            [302, 421, 314, 33, boarder_colour ],
            [285, 643, 291, 33, boarder_colour ],
            [550, 672, 25, 58, boarder_colour ],
            [428, 198, 40, 226, boarder_colour ],
            [583, 75, 36, 346, boarder_colour ],
            [610, 418, 268, 39, boarder_colour ],
            [583, 442, 46, 129, boarder_colour ],
            [560, 714, 285, 17, boarder_colour ],
            [824, 567, 30, 151, boarder_colour ],
            [832, 701, 20, 68, boarder_colour ],
            [963, 291, 62, 566, boarder_colour ],
            [720, 222, 303, 70, boarder_colour ],
            [1011, 226, 274, 63, boarder_colour ],
            [1522, 78, 30, 148, boarder_colour ],
            [1551, 193, 53, 33, boarder_colour ],
            [1598, 194, 35, 495, boarder_colour ],
            [1350, 640, 247, 49, boarder_colour ],
            [1148, 448, 64, 311, boarder_colour ],
            [1388, 81, 36, 434, boarder_colour ],              
              ]

        for block in boarders:
            block = Block( block[0], block[1], block[2], block[3], block[4] )
            self.block_list.add( block )            
        # Interactive objects 
        interactive_block = [
              ['Town',1668, 98,'Cave.png'],
              ]

        for block in interactive_block:
            block = Interactive_block( block[0], block[1], block[2], block[3] )
            self.interactiveblock_list.add( block )

def change_area(area=0):
    # used to change area's possible improvement here
        global current_area
        area_list = []
        area_list.append( Main_town( Player.player ) )
        area_list.append( Cave1( Player.player ) )
        current_area_number = area
        current_area = area_list[ current_area_number ]
        Player.player.set_area( current_area )

# general text template
def text(text, font):
    textSurface = font.render(text, True, black)
    return textSurface, textSurface.get_rect()

# button template that uses mouse events
def button(msg,x,y,w,h,ic,ac,action=None):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    #print(click)
    if x+w > mouse[0] > x and y+h > mouse[1] > y:
        pygame.draw.rect(window, ac,(x,y,w,h))
        if click[0] == 1 and action != None:
            action()
    else:
        pygame.draw.rect(window, ic,(x,y,w,h))
    smallText = pygame.font.SysFont("comicsansms", 18)
    textSurf, textRect = text(msg, smallText)
    textRect.center = ( (x+(w/2)), (y+(h/2)) )
    window.blit(textSurf, textRect)

def shopping():
    # .shop used to break while loop
    Player.player.shop = 0
    shop = True
    while ( shop ):
        for event in pygame.event.get():
            if ( event.type == pygame.QUIT ) or \
            ( event.type == pygame.KEYDOWN and \
            ( event.key == pygame.K_ESCAPE ) ):
                shop = False
                pygame.quit()
        if Player.player.shop == 1:
            break

        # shop interface                
        window.fill( white )
        largeText = pygame.font.SysFont('arial' ,55)
        TextSurf, TextRect = text("What would you like to do?", largeText)
        TextRect.center = ((display_width/2),(display_height/1.5))
        window.blit(TextSurf, TextRect)

        #shop buttons
        button("Potion Store",display_width*0.16,display_height*0.85,display_width*0.13,display_height*0.09,green,bright_green,potionshop)
        button("Return to Town",display_width*0.68,display_height*0.85,display_width*0.13,display_height*0.09,red,bright_red,end_shop)

        clock.tick( 15 )

        pygame.display.update()

def potionshop():
    # .shop used to break while loop
    Player.player.shop = 0
    shop = True
    while ( shop ):
        for event in pygame.event.get():
            if ( event.type == pygame.QUIT ) or \
            ( event.type == pygame.KEYDOWN and \
            ( event.key == pygame.K_ESCAPE ) ):
                shop = False
                pygame.quit()
        if Player.player.shop == 1:
            break

        # shop interface
        window.fill( grey )
        largeText = pygame.font.SysFont('arial' ,55)
        TextSurf, TextRect = text("Welcome to the potion store", largeText)
        TextRect.center = ((display_width/2),(display_height/1.5))
        window.blit(TextSurf, TextRect)

        #shop buttons
        button("Small potion",display_width*0.14,display_height*0.75,display_width*0.13,display_height*0.09,green,bright_green,None)
        button("medium potion",display_width*0.28,display_height*0.75,display_width*0.13,display_height*0.09,green,bright_green,None)
        button("large potion",display_width*0.42,display_height*0.75,display_width*0.13,display_height*0.09,green,bright_green,None)
        button("massive potion",display_width*0.56,display_height*0.75,display_width*0.13,display_height*0.09,green,bright_green,None)
        button("Return to Store",display_width*0.70,display_height*0.75,display_width*0.13,display_height*0.09,blue,bright_blue,go_shopping)
        button("Return to Town",display_width*0.42,display_height*0.85,display_width*0.13,display_height*0.09,red,bright_red,end_shop)

        clock.tick( 15 )                  

        pygame.display.update()

def go_shopping():
    # .shop used to break while loop
    Player.player.shop = 1
    shopping()

def end_shop():
    # .shop used to break while loop
    Player.player.shop = 1
    change_area()

if ( __name__ == "__main__" ):
    pygame.init()

    boarder = 20
    display_size = display_width, display_height = 700, 350
    window = pygame.display.set_mode( display_size )

    pygame.display.set_caption ( "Battle RPG ")

    Player.player = Player()
    Player.player.set_herostats()
    active_object_list = pygame.sprite.Group()
    active_object_list.add( Player.player )

    change_area()

    clock = pygame.time.Clock()
    frames_per_second = 60

    running = True
    while ( running ):
        for event in pygame.event.get():
            if ( event.type == pygame.QUIT ) or \
            ( event.type == pygame.KEYDOWN and \
            ( event.key == pygame.K_ESCAPE ) ):
                running = False


        # Update Functions
        Player.player.update(current_area.block_list,current_area.interactiveblock_list, event)
        event = None
        current_area.update()

        # Logic Testing
        current_area.run_viewbox()


        # Draw everything
        current_area.draw( window )

        active_object_list.draw( window )

        # Delay Framerate

        clock.tick( frames_per_second )


        # Update the screen                    

        pygame.display.update()

    pygame.quit()

这里是代码(对不起,它很长),我知道这可能是我从教程中得到的一些状态,我遵循了我偶然发现的其余部分。你们在几乎不知道所有谷歌搜索的情况下帮助了我很多,我一直希望有一个 stackoverflow 结果。无论如何,我的主要目标是:

  1. 将播放器类从主脚本中移除到它自己的模块中,同时仍然可以通过区域。

然后 2.慢慢删除其余的代码(希望它会是相同的解决方案) 3.摆脱我用于current_area的全局。

感谢阅读并希望能解决,我将继续碰壁,直到我解决这个问题,并将回复我找到的任何解决方案。

【问题讨论】:

    标签: python import module scope pygame


    【解决方案1】:

    将 Player 放在一个名为 player.py 的文件中,将 Block 和 InteractiveBlock 放在一个名为 blocks.py 的文件中,将 Area 的东西放在 area.py 中。在每个 import pygame 及其使用的任何类的顶部,因此如果在 Area 类中使用了 player,您可以执行 import player,在这种情况下您必须编写 the_player = payer.Player() 或from player import Player,在这种情况下你可以写 the_player = Player()。

    将其余部分放在一个名为 main.py 的文件中,然后导入其中的所有内容,包括玩家、区域和方块。

    您对它们的引用与其他类相同。

    Current_area 肯定是 Player 类变量。

    【讨论】:

    • Current_area 成为 Player 变量创造了奇迹,我看过了。尽管在我移动播放器类并将导入到主文件和 player.py 文件之后,我仍然遇到导入问题。当我进入洞穴或商店时(当我改变位置时),我仍然会遇到崩溃,我得到一个 NameError:name 'player' is not defined。我不明白我需要在哪里定义播放器以允许它通过我的主脚本中的所有函数和类进行访问。感谢您的回复和帮助
    【解决方案2】:

    在 marienbad 的帖子的帮助下,我弄清楚了问题所在。正如 marienbad 关于如何导入端口类的建议,我将所有类删除到自己的模块中。留给我一个清洁的工作空间。我尝试了一些事情,并注意到在我的 Area 类中我正在传递播放器对象,因此我尝试对 change_area 函数执行相同的操作,并且它可以正常工作,因此修改后的代码对于其他解决他的问题的人来说都是这样的。

    interaction_list = pygame.sprite.spritecollide(self, interact, False )
    # interactive block events
    for interaction in interaction_list:
        print(interaction.name)
        self.vertical_speed = 0
        self.horizontal_speed = 0
        if interaction.name == 'cave1':
            change_area(player, 1)
        if interaction.name == 'Town':
            change_area(player)
        if interaction.name == 'shops':
            shopping(player)
    

    这就是我现在将播放器传递给函数的方式

    def shopping(player_object):
        # .shop used to break while loop
        player = player_object
        clock = pygame.time.Clock()
        player.shop = 0
        shop = True
        while ( shop ):
            for event in pygame.event.get():
                if ( event.type == pygame.QUIT ) or \
                ( event.type == pygame.KEYDOWN and \
                ( event.key == pygame.K_ESCAPE ) ):
                    shop = False
                    pygame.quit()
            if player.shop == 1:
                break
    

    这就是函数接收播放器类的方式,从而保持它的定义。现在这可以按我的意图工作,也许是一个更好的解决方案,但它是可行的,我理解。谢谢大家

    【讨论】:

      【解决方案3】:

      我认为错误在这里:Player.player - Player 是一个类,并且没有 player 变量。您只需要在 main 中执行 the_player = Player()the_player = player.Player() 即可实例化播放器对象,具体取决于您如何从播放器模块导入 Player 类,如上述答案。尽量不要给变量与模块同名。还有Player_object是什么?

      【讨论】:

      • 我已经在代码中更改了 player 现在只是 player 而不是 player.player 。这是在代码中,因为我认为它可能会解决问题。 Player_object 只是我传递给 Area 类的变量的名称。可以改成播放器,方便理解。我也确实解决了我的问题,解决方案如下。感谢您的时间和精力
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-19
      • 2014-07-04
      • 1970-01-01
      • 2014-01-08
      • 2021-11-18
      • 1970-01-01
      相关资源
      最近更新 更多