Been making my first PyGame project and previously after clicking on "start game" button it would take user to the gameplay window, whereas now it claims gameplay is undefined. I have tried debugging but am relatively new to pygame, could somebody explain a solution to this issue? thanks. Below is the code:
# 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()
I have already attempted moving the functions into different files and renaming them, this seems not to have worked. (sorry for poor English and coding works, I'm new to both)
Multiple Python files may not work correctly if they are just loosely put together in the same directory.
In particular, if you try to run a file from a parent directory - like
python game/mygame.pywhengame_play.pyis also in the game directory, it will fail.The best approach is to turn your project in a minimalist Python package, and install it with the editable flag. Then, from one file, you will be able to type things like
from .game_play import *(notice the "." prefixing "game_play" here).As a side note, I should also remind you that
... import *is usually a poor choice - in general, it is better to either import the module, and keep it as prefix, or import names from it explicitly.Anyway, if your files are inside a "game" directory, to create a game-package you have to do this: on the parent directory of "game", (i.e. in the same directory "game" is listed) create a file with 0 bytes named "pyproject.toml" This will create a project spec using the default values, which are the classic "setuptools" and "wheel", and will not need a "setup.py" file. Then, in the same directory, type on the O.S. shell
pip install -e .(you'd better have a proper virtualenv for that, but it will work otherwise, and that is a separate issue).From that point on, "game" is an installed package, and you can do things like
import game.game_play. If you have a file named__main__.pyinside the game folder it will function as the entry point when you typepython -m gamefrom the shell prompt.