Pygame Menu: calling another .py file - python

I'm learning pygame by helping a friend make a Menu for his interactive TicTacToe game. It's simple enough, a title and 2 buttons for starting and exiting the game. He has the game done, I just need to link my menu to his game.
After fiddling with pygame, I finally finished making the images appear in the pygame window(I never thought that seeing text appear in a blank window for the first time could look so beautiful! T^T). I believe that the next step is to make the images act like buttons to call another python file.
Here is my simple code for the menu:
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
resX, resY = 640, 480
windowObj = pygame.display.set_mode((resX,resY))
titleImg = pygame.image.load("title.png")
startImg = pygame.image.load("start.png")
exitImg = pygame.image.load("exit.png")
def width_center(img):
"""For horizontally setting an image."""
return resX/2 - x(img)/2
def height_center(img):
"""For vertically setting an image."""
return resY/2 - y(img)/2
def x(img):
"""Width of object."""
return img.get_width()
def y(img):
"""Height of object."""
return img.get_height()
while True:
pygame.display.update()
windowObj.fill((255,255,255))
windowObj.blit(titleImg,(width_center(titleImg), 30))
#This I want to link:
windowObj.blit(startImg,(width_center(startImg),height_center(startImg)-10))
windowObj.blit(exitImg,(width_center(exitImg),height_center(exitImg)+y(startImg)))
for i in pygame.event.get():
if i.type == QUIT:
exit()
Simply put, I want to know how to make the startImg call TTTClick.py. Should there also be a certain format for his TTTClick.py?
Thanks :)

If it's one project, you can have 'title screen' state, and 'game' states.
Psuedo code:
class Game():
def __init__(self):
self.done = False
self.state = "title"
def game_draw(self):
# game draw
def game_update(self):
# game event handling, and physics
def title_draw(self):
# title draw
def title_update(self):
# on event click
self.state = "game"
def loop(self):
# main loop
while not self.done:
if state == 'title':
title_update()
title_draw()
elif state == 'game':
game_update()
game_draw()
if __name__ == "__main__":
game = Game()
game.loop()
Note: x, height_center ect. already exist in pygame.Rect
# if Surface
titleImage.get_rect().width
# if Sprite
titleImage.rect.width
There's more, rect.center, rect.centerx, see the full listing at http://www.pygame.org/docs/ref/rect.html

Related

Pygame_menu loop overrides the transition to another screen

Currently working on a backgammon game with eventual multiplayer support.
I have a mainmenu function that is working, and its exceptions and draws are handled by the mainloop() attribute. However, I have a button that, when pressed, should switch to the game with the backgammon board as the background.
The image appears briefly, then the main menu continues to run. I've tried to find ways to disable the menuloop using conditional statements to no avail.
Here is the full source code:
import sys, pygame, os, pygame_menu
width, height = 1280, 960
class mainBackgammonGame():
global width, height
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Bootleg Backgammon")
self.clock = pygame.time.Clock()
def updateScreen(self):
self.clock.tick(60)
pygame.display.flip()
def startGame():
surface = pygame.display.set_mode((width, height))
currPath = os.path.dirname(os.path.abspath(__file__))
boardPath = os.path.join(currPath, "images/mainboard.png")
boardImage = pygame.image.load(boardPath)
surface.blit(boardImage, (0,0))
pygame.display.flip()
def mainMenu():
surface = pygame.display.set_mode((width, height))
menu = pygame_menu.Menu(960, 1280, 'Welcome to Backgammon', theme = pygame_menu.themes.THEME_DARK)
menu.add_text_input("Your Name: ", default = "Joseph Smith")
menu.add_button('Play', mainBackgammonGame.startGame)
menu.add_button('Quit', pygame_menu.events.EXIT)
menu.mainloop(surface)
bg = mainBackgammonGame()
mainMenu()
while 1:
for event in pygame.event.get():
if event == pygame.QUIT:
exit()
bg.updateScreen()
Figured it out.
def mainMenu():
def disable():
menu.disable()
mainBackgammonGame.startGame()
surface = pygame.display.set_mode((width, height))
menu = pygame_menu.Menu(960, 1280, 'Welcome to Backgammon', theme = pygame_menu.themes.THEME_DARK)
menu.add_text_input("Your Name: ", default = "Joseph Smith")
menu.add_button('Play', disable)
menu.add_button('Quit', pygame_menu.events.EXIT)
menu.mainloop(surface)
Added a new function within the main menu function that initially disables the menu, then will call the startGame function that loads the screen.
I recommend reading the following example:
https://github.com/ppizarror/pygame-menu/blob/master/pygame_menu/examples/game_selector.py
You can see that the main part of that is having a main menu variable
main_menu: Optional['pygame_menu.Menu'] = None
Which gets initialized elsewhere in the code.
Additionally when it's time for that menu to leave, like if you want to move to the game after being in the menu then you want a function like this:
def start_game():
global menu
# Do stuff here
menu.disable()
Then in your pygame main loop you can guard the mainloop call like so:
if menu.is_enabled():
menu.mainloop(screen)

Pygame screen output not displaying

I'm working on a game project with Pygame. I'm only just beginning to work on the project and I'm kind of stuck. I have made three files each containing code to perform different functions. The first file - "alien_apocalypse.py"contains the class 'AlienApocalypse', which serves to start and monitor user events in the game and contains a few imported modules such as the game settings
(which is the 2nd file - 'game_settings.py') and the a class file which contains the all the attributes of one of the game characters 'knight.py'. I'm trying to run "alien_apocalypse.py" which is meant to display my character-knight and the bottom middle of the display but nothing shows up. I'm running it on a Mac with macOS Mojave, IDE is PyCharm. Here are the files:
File 1 - "alien_apocalypse.py":
import sys
import os
import pygame
from game_settings import GameSettings
from knight import Knight
class AlienApocalypse:
"""Overall class to manage game assets and behaviour"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = GameSettings()
drivers = ['directfb', 'fbcon', 'svgalib']
found = False
for driver in drivers:
if not os.getenv('SDL_VIDEODRIVER'):
os.putenv('SDL_VIDEODRIVER', driver)
try:
pygame.display.init()
except pygame.error:
print('Driver: {0} failed.'.format(driver))
continue
found = True
break
if not found:
raise Exception('No suitable video driver found!')
self.screen_window = pygame.display.set_mode((2880, 1800))
pygame.display.set_caption("Alien Apocalypse")
"""Setting background color"""
self.background_color = (230, 230, 255)
self.knight = Knight(self)
def run_game(self):
"""Start the main loop for the game"""
while True:
# Watch for keyboard and mouse actions
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Redraw the screen during each pass through the loop.
self.screen_window.fill(self.settings.background_color)
self.knight.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
if __name__ == "__main__":
"""Make a game instance, and run the game"""
ac = AlienApocalypse()
ac.run_game()
File 2 - game_settings.py
class GameSettings:
"""This class stores all the game settings"""
def __init__(self):
"""Initialize the game's settings attributes"""
# Screen settings
self.screen_width = 2880
self.screen_height = 1800
self.background_color = (230, 230, 255)
File 3 knight.py
import pygame
class Knight:
"""A class that manages the character knight"""
def __init__(self, ac_game):
"""Initialize the knight and set its starting position."""
self.screen_window = ac_game.screen_window
self.screen_window_rect = ac_game.screen_window.get_rect()
# Load the character - Knight image and get its rect.
image_file = "/Users/johnphillip/Downloads/craftpix-891165-assassin" \
"-mage-viking-free-pixel-art-game-heroes/PNG/Knight" \
"/knight.bmp "
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
# Start each new character at the bottom center of the screen.
self.rect.midbottom = self.screen_window_rect.midbottom
def blitme(self):
"""Draw the character at its current location."""
self.screen_window.blit(self.image, self.rect)
Issues I detected so far (now the window is displayed):
You have to set proper video driver
Then you have to initialize pygame.display
You were calling the run_game() function inside the class (ac.run_game()), not the other.
The run_game() inside the class does nothing (pass)
You have to replace the current run_game() inside the class with the one which is outside, so you can access "self things" like variables and functions.
You can not equal self to None as default value if not present (self is the class itself and everything it contains, so if you equal it to None you are "killing" yourself (the class)!!!)
Your alien_apocalypse.py could look like this:
import pygame
from game_settings import GameSettings
from knight import Knight
class AlienApocalypse:
"""Overall class to manage game assets and behaviour"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = GameSettings()
drivers = ['windib', 'directx']
found = False
for driver in drivers:
if not os.getenv('SDL_VIDEODRIVER'):
os.putenv('SDL_VIDEODRIVER', driver)
try:
pygame.display.init()
except pygame.error:
print('Driver: {0} failed.'.format(driver))
continue
found = True
break
if not found:
raise Exception('No suitable video driver found!')
self.screen_window = pygame.display.set_mode((2880, 1800))
pygame.display.set_caption("Alien Apocalypse")
"""Setting background color"""
self.background_color = (230, 230, 255)
self.knight = Knight(self)
def run_game(self):
"""Start the main loop for the game"""
while True:
# Watch for keyboard and mouse actions
# for event in pygame.event.get():
# if event.type == pygame.QUIT:
# sys.exit()
# Redraw the screen during each pass through the loop.
self.screen_window.fill(self.settings.background_color)
self.knight.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
if __name__ == "__main__":
"""Make a game instance, and run the game"""
ac = AlienApocalypse()
ac.run_game()

Python Basic control flow errors

When I run this code, it enters a while loop and checks every turn whether or not on_title_screen==True. If it is true, the program will continue to check for input, but if it is false, the loop will refresh the screen and begin the game. However, when start is clicked, and on_title_screen=False, the game still captures mouse input, and does not display the bird that it should.
import random
import pygame
from pygame import *
import math
import sys
#Presets for window
size=width,height=500,500
Ag=-9.80665
clock = pygame.time.Clock()
white=(255,255,255)
blue=(0,0,255)
red=(255,0,0)
gray_bgColor=(190,193,212)
#Initialise pygame Surface as screen
pygame.init()
pygame.font.init()
screen=pygame.display.set_mode(size)
pygame.display.set_caption("Flappy Kid")
#Game Presets
vY=0
xPos,yPos=200,100
score=0
on_title_screen=True
def falling_loop():
for event in pygame.event.get():
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_UP:
vY=-10
if yPos>height-50:
yPos=100
vY+=1
yPos+=vY
class graphics():
#Holds the methods for loading/displaying graphics
def load_images(self):
#Loads the background and sprite images
self.background_image=pygame.image.load("flappy_background.png").convert()
self.bird_image=pygame.image.load("flappy_sprite.jpg").convert()
screen.set_colorkey(white)
self.birdHitBox=self.bird_image.get_rect()
def show_background(self):
#blits the background
screen.blit(self.background_image,[0,0])
def refresh_display(self):
#updates the display
screen.blit(self.background_image,[xPos,yPos],self.birdHitBox)
falling_loop()
screen.blit(self.bird_image,[xPos,yPos])
class titleScreen():
#Holds the methods for the title screen/menu
def title(self):
#Sets up the title
titleText="Flappy Game"
titlePos=(0,0)
currentFont=pygame.font.SysFont("arialms",30,bold=True,italic=True)
renderTitle=currentFont.render(titleText,1,blue,gray_bgColor)
self.titlex,self.titley=currentFont.size(titleText)
screen.blit(renderTitle,titlePos)
def start(self):
#Sets up the start Button
startText="Start Game"
self.startPos=(0,self.titley)
currentFont=pygame.font.SysFont("arialms",25,bold=False,italic=False)
renderStart=currentFont.render(startText,1,blue,gray_bgColor)
self.startx,self.starty=currentFont.size(startText)
self.start_rect = pygame.Rect(self.startPos[0],self.titley,self.startx,self.starty)
screen.blit(renderStart,self.startPos)
def quit(self):
#Sets up the quit button
quitText="Quit"
self.quitPos=(0,self.starty+self.titley)
currentFont=pygame.font.SysFont("arialms",25,bold=False,italic=False)
renderQuit=currentFont.render(quitText,1,red,gray_bgColor)
self.quitx,self.quity=currentFont.size(quitText)
self.quit_rect = pygame.Rect(self.quitPos[0],self.titley+self.starty,self.quitx,self.quity)
screen.blit(renderQuit,self.quitPos)
def get_click(self):
#Gets mouse click and processes outcomes
for event in pygame.event.get():
if event.type==pygame.MOUSEBUTTONDOWN:
x,y=pygame.mouse.get_pos()
#Tests for start:
if self.start_rect.collidepoint(x,y):
print("start")
on_title_screen=False
elif self.quit_rect.collidepoint(x,y):
print("quit")
sys.exit()
titleC=titleScreen()
graphicsC=graphics()
def setupTitle():
#bundles all title_screen functions
titleC.title()
titleC.start()
titleC.quit()
def main():
graphicsC.load_images()
graphicsC.show_background()
setupTitle()
while True:
clock.tick(30)
if on_title_screen==False:
graphicsC.refresh_display()
elif on_title_screen==True:
titleC.get_click()
pygame.display.flip()
main()
I think #TessellatingHeckler is right, on_title_screen is a shadow variable, not the same.
In this code, there is nowhere that on_title_screen (global) could ever be set to False.
A more powerful answer though is to explain how to find the problem. I strongly recommend using pdb or ipdb. In this case, I would put one just inside of the while loop and make sure that the variables are what I think they should be.

USB controller will not work in Pygame

I have a USB controller that I have mapped to be the arrow keys on the keyboard, outside of gaming programs (as in, you can use it as the normal arrow keys on the keyboard). I did this using the program ControlMK. My Pygame program will not recognize the controller as the keyboard. When I try to use the Joystick modules, the program does not function properly.
Here is my code:
import pygame, sys, time, random
from pygame.locals import *
# set up pygame
try:
pygame.init()
pygame.display.init()
pygame.mixer.init(size=8, buffer=2048)
pygame.mixer.get_init
except:
print "error in init\n"
white=(255,255,255)
screen=pygame.display.set_mode((800,600), pygame.RESIZABLE)
class Loadsound:
def __init__(self, name, key, sound_file):
self.name = name
self.name=key
self.sound = pygame.mixer.Sound(sound_file)
sound_left=[]
sound_left.append(Loadsound("left","K_LEFT", "left.wav"))
sound_right=[]
sound_right.append(Loadsound("right","K_RIGHT", "right.wav"))
sound_up=[]
sound_up.append(Loadsound("up","K_UP","up.wav"))
sound_down=[]
sound_down.append(Loadsound("down","K_DOWN","down.wav"))
while True:
for i in pygame.event.get():
if i.type==QUIT:
exit()
pressed=pygame.key.get_pressed()
if pressed[K_LEFT]:
for left in sound_left:
left.sound.play()
elif pressed[K_RIGHT]:
for right in sound_right:
right.sound.play()
elif pressed[K_UP]:
for up in sound_up:
up.sound.play()
elif pressed[K_DOWN]:
for down in sound_down:
down.sound.play()
Thank you for the help!
Edit:
I have been trying to use the Joystick module. Here is where I was taking example code from:
http://www.pygame.org/wiki/Joystick_analyzer
I edited my code to include some of it, but it still does not work. Here is my edited code:
import pygame, sys, time, random
from pygame.locals import *
from pygame import *
# set up pygame
try:
pygame.init()
pygame.display.init()
pygame.mixer.init(size=8, buffer=2048)
pygame.mixer.get_init
except:
print "error in init\n"
white=(255,255,255)
screen=pygame.display.set_mode((800,600), pygame.RESIZABLE)
class Loadsound:
def __init__(self, name, key, sound_file):
self.name = name
self.name=key
self.sound = pygame.mixer.Sound(sound_file)
sound_left=[]
sound_left.append(Loadsound("left","K_LEFT", "left.wav"))
sound_right=[]
sound_right.append(Loadsound("right","K_RIGHT", "right.wav"))
sound_up=[]
sound_up.append(Loadsound("up","K_UP","up.wav"))
sound_down=[]
sound_down.append(Loadsound("down","K_DOWN","down.wav"))
def __init__(self):
pygame.joystick.init()
self.my_joystick = None
self.joystick_names = []
for i in range(0, pygame.joystick.get_count()):
self.joystick_names.append(pygame.joystick.Joystick(i).get_name())
print self.joystick_names
if (len(self.joystick_names) > 0):
self.my_joystick = pygame.joystick.Joystick(0)
self.my_joystick.init()
#max_joy = max(self.my_joystick.get_numaxes(), self.my_joystick.get_numbuttons(), self.my_joystick.get_numhats()
while True:
for i in pygame.event.get():
pygame.event.pump()
if i.type==QUIT:
exit()
#pygame.joystick.Joystick(0)
#Joystick.init()
pressed=pygame.key.get_pressed()
#pressed_j=Joystick.get_hat()
def check_hat(self, p_hat):
if (self.my_joystick):
if (p_hat,self.my_joystick.get_numhats()):
return self.my_joystick.get_hat(p_hat)
return (0, 0)
if check_hat==(-1,0):
#if pressed[K_LEFT]:
for left in sound_left:
left.sound.play()
elif pressed[K_RIGHT]:
for right in sound_right:
right.sound.play()
elif pressed[K_UP]:
for up in sound_up:
up.sound.play()
elif pressed[K_DOWN]:
for down in sound_down:
down.sound.play()
For clarity, my code DOES work when using the keyboard. However, it does not translate to the controller which is mapped to the same keys.
You'll need to use Pygame's Joystick module, or something similar, which has method's such as: Joystick.get_init(), which tells if the joystick is initialized in Pygame, and Joystick.get_axis(axis_number) which returns the Joystick's position, as a float, along a given axis axis_number. ControlMK is likely mapping the joystick to key inputs at too high of a level to interact with Pygame, though there may be some way to change that, its documentation seems limited.
Try this:
import pygame
pygame.init()
print "Joystics: ", pygame.joystick.get_count()
my_joystick = pygame.joystick.Joystick(0)
my_joystick.init()
clock = pygame.time.Clock()
while 1:
for event in pygame.event.get():
print my_joystick.get_axis(0), my_joystick.get_axis(1)
clock.tick(40)
pygame.quit ()

pygame code organization

I've started to learn making games with python/pygame and as though it is easy to make a working game quickly in pygame, there's no real tutorial on how to organize the code in a sensible way.
On the pygame tutorials page, I've found 3 ways to do it.
1- No use of classes, for small projects
2- MVC ruby-on-rails kind of structure but without the rails framework which results in something overly complicated and obscure (even with OO programming and rails knowledge)
3- C++-like structure as follows: (clean and intuitive but maybe not very much python-like?)
import pygame
from pygame.locals import *
class MyGame:
def __init__(self):
self._running = True
self._surf_display = None
self.size = self.width, self.height = 150, 150
def on_init(self):
pygame.init()
self._display_surf = pygame.display.set_mode(self.size)
pygame.display.set_caption('MyGame')
#some more actions
pygame.display.flip()
self._running = True
def on_event(self, event):
if event.type == pygame.QUIT:
self._running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self._running = False
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1:
print event.pos
def on_loop(self):
pass
def on_render(self):
pass
def on_cleanup(self):
pygame.quit()
def on_execute(self):
if self.on_init() == False:
self._running = False
while( self._running ):
for event in pygame.event.get():
self.on_event(event)
self.on_loop()
self.on_render()
self.on_cleanup()
if __name__ == "__main__" :
mygame = MyGame()
mygame.on_execute()
I'm used to make SDL games in C++ and I use this exact structure but I'm wondering if it's usable for both small and large projects or if there's a cleaner way in pygame.
For example I found a game organized like this:
imports
def functionx
def functiony
class MyGameObject:
class AnotherObject:
class Game: #pygame init, event handler, win, lose...etc
while True: #event loop
display update
It also looks very well organized and easy to follow.
Which structure should I use consistently in all my projects so as to have a clean code usable in small and large games?
I'd also suggest maybe using comments (as dreary as it seems as first) for dividing your work. As an example:
import pygame, random, math
## CLASSES ----------------------------------------------
class Ball():
def __init__(self, (x,y), size):
"""Setting up the new instance"""
self.x = x
self.y = y
self.size = size
## FUNCTIONS --------------------------------------------
def addVectors((angle1, length1), (angle2, length2)):
"""Take two vectors and find the resultant"""
x = math.sin(angle1) * length1 + math.sin(angle2) * length2
y = math.cos(angle1) * length1 + math.cos(angle2) * length2
## INIT -------------------------------------------------
width = 600
height = 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("S-kuru")
And so on.
As another option to consider, have you thought about using sub-modules? They're just other Python files (.py) where you place commonly-used functions.
def showText(passedVariable):
print passedVariable
return
This new file is the imported, just as math or random would be and the function used as such:
import mySubModule
mySubModule.showText("Hello!")
But that's just how I work. Absolutely follow what you can understand, not just now but next week or year also.
Do what you can follow. If you can make sense of the code you posted, then that's what you should use. If a different structure feels more natural, use that instead.

Categories