PyGame - 其他文件中的函数未定义

2 投票
1 回答
34 浏览
提问于 2025-04-14 17:35

我正在做我的第一个PyGame项目,之前点击“开始游戏”按钮后会进入游戏窗口,但现在却说游戏未定义。我尝试了调试,但对pygame还比较陌生,有人能解释一下这个问题的解决办法吗?谢谢。下面是代码:

# main.py

import pygame
import sys

from gameplay import *
pygame.init()

#CONSTANTS
screen_width = 800
screen_height = 800
running = True
sky_blue = (102, 230, 225)
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 255)
red = (255, 0, 0)
green = (0, 255, 0)
screen = pygame.display.set_mode((screen_height,screen_width))


############# SOURCED FROM: stackoverflow (https://stackoverflow.com/questions/47855725/pygame-how-can-i-allow-my-users-to-change-their-input-keys-custom-keybinding)
def create_key_list(input_map):
  """A list of surfaces of the action names + assigned keys, rects and the actions."""
  font = pygame.font.Font(None, 50)
  key_list = []
  for y, (action, value) in enumerate(input_map.items()):
    surf = font.render('{}: {}'.format(action, pygame.key.name(value)), True, red)
    rect = surf.get_rect(topleft=(40, y*40+20))
    key_list.append([surf, rect, action])
  return key_list


def assignment_menu(input_map):
  """Allow the user to change the key assignments in this menu.

  The user can click on an action-key pair to select it and has to press
  a keyboard key to assign it to the action in the `input_map` dict.
  """
  selected_action = None
  key_list = create_key_list(input_map)
  while True:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()
      elif event.type == pygame.KEYDOWN:
        if selected_action is not None:
          # Assign the pygame key to the action in the input_map dict.
          input_map[selected_action] = event.key
          selected_action = None
          # Need to re-render the surfaces.
          key_list = create_key_list(input_map)
        if event.key == pygame.K_ESCAPE:  # Leave the menu.
          # Return the updated input_map dict to the main function.
          return input_map
      elif event.type == pygame.MOUSEBUTTONDOWN:
        selected_action = None
        for surf, rect, action in key_list:
          # See if the user clicked on one of the rects.
          if rect.collidepoint(event.pos):
            selected_action = action

      screen.fill(sky_blue)
      # Blit the action-key table. Draw a rect around the
      # selected action.
      for surf, rect, action in key_list:
        screen.blit(surf, rect)
        if selected_action == action:
          pygame.draw.rect(screen, red, rect, 2)

    pygame.display.update()




def draw_button(text, x, y, width, height, colour): # draw button function
  font_button = pygame.font.Font(None, 50)
  pygame.draw.rect(screen, colour, (x, y, width, height))
  text_surface = font_button.render(text, True, white)
  text_rect = text_surface.get_rect(center=(x + width / 2, y + height / 2))
  screen.blit(text_surface, text_rect)


def bootup_menu():  # main menu function
  pygame.display.set_caption("Mimit | Main Menu")
  input_map = {"RIGHT": pygame.K_d, "LEFT": pygame.K_a, "UP": pygame.K_w, "DOWN": pygame.K_s, "DASH": pygame.K_LSHIFT, "ATTACK": pygame.K_SPACE, "INVENTORY" : pygame.K_i} ######################
  while running:
    screen.fill(sky_blue)
    font_title = pygame.font.Font(None, 125)
    text_surface = font_title.render("Mimit!", True, pygame.Color((25, 25, 255)))
    text_rect = text_surface.get_rect()
    text_rect.midtop = (screen_width // 2, 88)
    screen.blit(text_surface, text_rect) # renders title
    draw_button("Start Game!", 200, 300, 400, 48, (25, 25, 255))
    draw_button("Settings", 200, 480, 400, 48, (25, 25, 255))
    draw_button("Quit", 200, 660, 400, 48, (25, 25, 255)) # render buttons
    for event in pygame.event.get(): # give buttons functionality
      if event.type == pygame.QUIT:
        pygame.quit() 
        sys.quit() 
      if event.type == pygame.MOUSEBUTTONDOWN:
        mouse_x, mouse_y = pygame.mouse.get_pos()
        if (200 <= mouse_x <= 600) and (300 <= mouse_y <= 348): # start game
          gameplay(running, input_map)
          #character_preset_menu()
          pass
        elif (200 <= mouse_x <= 600) and (480 <= mouse_y <= 528): # settings
          input_map = assignment_menu(input_map)
        elif (200 <= mouse_x <= 600) and (660 <= mouse_y <= 708): # quit
          pygame.quit()
          sys.exit()
      if event.type == pygame.K_ESCAPE:
        input_map = assignment_menu(input_map)


    pygame.display.update() # updates the screen using the variables current states above



def character_preset_menu():
  pass



bootup_menu() # loads bootup menu
# gameplay.py

import pygame
import sys
import math
import time
from main import *
import csv

pygame.init()

#consts
screen_width = 800
screen_height = 800
sky_blue = (102, 230, 225)
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 255)
red = (255, 0, 0)
green = (0, 255, 0)
map_size = 40
tile_size = 20
PLAYER_SPRITE_STATIONARY = pygame.image.load("assets/PLAYER_SPRITE_STATIONARY.png")
PLAYER_SPRITE_STATIONARY = pygame.transform.scale(PLAYER_SPRITE_STATIONARY, (20, 20))
PLAYER_SPRITE_MOVING = pygame.image.load("assets/PLAYER_SPRITE_STATIONARY.png")
PLAYER_SPRITE_MOVING = pygame.transform.scale(PLAYER_SPRITE_STATIONARY, (20, 20))

#map
MAP = (
      "########################################"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "#                                      #"
      "########################################"
)

#Initialise the pygame window
screen = pygame.display.set_mode((screen_height,screen_width))
clock = pygame.time.Clock()

#Draw the map based on the MAP string, utilising loops
def draw_map():
  for row in range(map_size):
    for col in range(map_size): # iterate over each row/column
      square = row * map_size + col
      pygame.draw.rect(
        screen,
        (230,15,15) if MAP[square] == "#" else (0,0,255), # Uses the character flags given
        (col*tile_size ,row*tile_size , tile_size, tile_size)
      )


def user_position_check(self, position_change): 
  # Up is minus 40, Down is plus 40, left is minus 1, right is plus 1
  next_tile = MAP[self.tile + position_change]
  if next_tile == "#": # Checks the flags in MAP
    return False
  else:
    return True


class USER(pygame.sprite.Sprite): # Grouping the users sprite as its own class
  def __init__(self, x, y):
    super().__init__() 
    self.image = PLAYER_SPRITE_STATIONARY
    self.rect = self.image.get_rect()
    self.rect.topleft = (x, y)
    self.speed = 20
    self.tile = 41
    self.inventory = {}
    self.selected_item = None
    self.stamina = 100
    self.health = 100
    
    # Self variables update globally and store values about the user only


  def update(self, keys, input_map): # Takes key binds from input map and responds with appropriate movement
    if keys[input_map["LEFT"]]:
      position_change = (-1)
      if user_position_check(self, position_change):
        self.tile += position_change # position change changes the vector position of the user, not the physical location
        self.rect.x -= self.speed # changes the physical location of the sprite
        self.image = PLAYER_SPRITE_MOVING # Image of moving, must be changed once graphics are developed
      time.sleep(0.2)
    elif keys[input_map["RIGHT"]]:
      position_change = 1
      if user_position_check(self, position_change):
        self.tile += position_change
        self.rect.x += self.speed
        self.image = PLAYER_SPRITE_MOVING
      time.sleep(0.2)
    elif keys[input_map["UP"]]:
      position_change = (-40)
      if user_position_check(self, position_change):
        self.tile += position_change
        self.rect.y -= self.speed
        self.image = PLAYER_SPRITE_MOVING
      time.sleep(0.2)
    elif keys[input_map["DOWN"]]:
      position_change = 40
      if user_position_check(self, position_change):
        self.tile += position_change
        self.rect.y += self.speed
        self.image = PLAYER_SPRITE_MOVING
      time.sleep(0.2)
    elif keys[input_map["INVENTORY"]]:
      inventory_open(self, input_map)
    else:
      position_change = 0 # stationary else statement
      self.image = PLAYER_SPRITE_STATIONARY


  def inventory_open(self, input_map, screen):
    inventory_width, inventory_height = 640, 220
    selected = None
    while selected == None:
      pygame.draw.rect(screen, (0,0,0), (80, 290, inventory_width, inventory_height))
      screen.blit()


# Create USER sprite using groupings
user = USER(20, 20) # Spawn point in pixels (1st walkable tile)
user_sprites = pygame.sprite.Group()
user_sprites.add(user)

def gameplay(running, input_map):
  pygame.display.set_caption("Gameplay!")
  screen.fill(sky_blue)
  while running: # new game instance
    draw_map()
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()
    keys = pygame.key.get_pressed() # Checks state of all keys, returns True when a key is pressed
    user_sprites.update(keys, input_map) # Updates based on this check ^
    user_sprites.draw(screen)


    pygame.display.update()

我已经尝试把函数移动到不同的文件里并重命名,但似乎没有效果。(抱歉我的英语和代码写得不好,我对这两者都很新)

1 个回答

0

如果你把多个Python文件随便放在同一个文件夹里,它们可能不会正常工作。

特别是,如果你尝试从上级目录运行一个文件,比如在有game_play.py的情况下运行python game/mygame.py,这会失败。

最好的办法是把你的项目做成一个简约的Python包,并用可编辑的方式安装。这样,你就可以在一个文件里输入类似from .game_play import *的代码(注意这里的"game_play"前面有个".")。

顺便提一下,我要提醒你,... import *通常不是个好选择。一般来说,最好是导入整个模块,并在前面加上模块名,或者明确地导入你需要的名称。

无论如何,如果你的文件在一个叫“game”的文件夹里,要创建一个游戏包,你需要这样做:在“game”的上级目录(也就是“game”所在的目录)创建一个名为“pyproject.toml”的0字节文件。这将使用默认值创建一个项目规范,这些默认值是经典的“setuptools”和“wheel”,而且不需要“setup.py”文件。

然后,在同一个目录下,在操作系统的命令行输入pip install -e .(最好有一个合适的虚拟环境,但即使没有也能工作,这又是另一个话题)。

从那时起,“game”就成了一个已安装的包,你可以像这样使用import game.game_play。如果在game文件夹里有一个名为__main__.py的文件,当你在命令行输入python -m game时,它会作为入口点运行。

撰写回答