Call function on button click multiple time? - python

my python/Pygame program starts with a button that calls a function and a button that quits the game. I wanna push this first button, then a function gets called one time and after that, it should return to the beginning button screen and let me call the function again by button click. How can I do that? Currently i am only able to click the button, call the function and then the game ends. In the code below you see the most important parts of the code.
def function():
....
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
done = False
def quit_game(): # A callback function for the button.
nonlocal done
done = True
button1 = create_button(100, 100, 250, 80, 'function', function)
button2 = create_button(100, 200, 250, 80, 'quit', quit_game)
# A list that contains all buttons.
button_list = [button1, button2]
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# This block is executed once for each MOUSEBUTTONDOWN event.
elif event.type == pygame.MOUSEBUTTONDOWN:
# 1 is the left mouse button, 2 is middle, 3 is right.
if event.button == 1:
for button in button_list:
# `event.pos` is the mouse position.
if button['rect'].collidepoint(event.pos):
# Increment the number by calling the callback
# function in the button list.
button['callback']()
screen.fill(WHITE)
for button in button_list:
draw_button(button, screen)
pygame.display.update()
clock.tick(30)
main()

What you need here is to isolate each screen/game loop into its own special function:
So, for my button screen I can make a function like this:
def title():
button1 = create_button(100, 100, 250, 80, 'game', game) # This will call the game function later in the file
button2 = create_button(100, 200, 250, 80, 'quit_game', quit_game)
# A list that contains all buttons.
button_list = [button1, button2]
# And so on with the rest of the code...
For the main game, you can do the same:
def game():
button1 = create_button(100, 100, 250, 80, 'Exit', title) # This button will return you to the title
# And whatever else you need
After that, at the end of the file, you can add this:
if __name__ == '__main__':
pygame.init()
title() # Call the title first, and then further functions
pygame.quit()
You will have to note that when you activate the buttons callback, a return afterwards is needed in order to deload that screen, otherwise you would just be layering game loops on top of eachother.
So, during the event loop:
if event.button == 1:
for button in button_list:
# `event.pos` is the mouse position.
if button['rect'].collidepoint(event.pos):
# Increment the number by calling the callback
# function in the button list.
button['callback']()
return # Leave the function now that the new one is called.

Related

Pygame class spawns rect and then it dissapears, how do I get it to stay?

So I'm trying to implement a class for a basic game. It woked without the class but now instead of spawning the "coin" it pops up and then immediatly dissapears. No idea as it's in the main loop. I have a moving "player" that works fine.
Here's my code:
class Coin_Class:
def __init__(self):
coin_1 = pygame.Rect(425, 30, 40, 40)
pygame.draw.rect(WIN, YELLOW, coin_1)
pygame.display.update()
# def coin_collect():
# if player.colliderect():
# coin_1.x = random.randint(0, 800)
# coin_1.y = random.randint(0, 250)
# pygame.event.post(pygame.event.Event(coin_collected))
# global score
# score += 1
# print(score)
coin_class = Coin_Class()
# main function loop
def main():
score_check = 0
clock = pygame.time.Clock()
run = True
while run:
# game speed
clock.tick(FPS)
# initialise pygame
pygame.init()
# checking all the events in pygame and looping them
for event in pygame.event.get():
# checking quit function is pressed
if event.type == pygame.QUIT:
run = False
pygame.quit()
exit()
keys_pressed = pygame.key.get_pressed() # recognise key presses
player_movement(keys_pressed, player) # movement function
draw_window() # create window function
coin_class
main()
# runs the main file
if __name__ == "__main__":
main()
Add a draw method to the coin class (also class names per PEP 8 should be in CapitalCase not Capital_Snake_Case so CoinClass):
class Coin_Class:
def __init__(self):
self.coin_1 = pygame.Rect(425, 30, 40, 40)
...
def draw(self):
pygame.draw.rect(WIN, YELLOW, self.coin_1)
And in the loop instead of using
coin_class
you would now use
coin_class.draw()
The rest can stay the same, except remove pygame.init() from the loop and put it somewhere at the start of the code after imports

Pygame multiple PS3 controllers BUTTON numbering is not consistent

I'm writing pygame-based code for two players / two PS3 controllers.
My problem is that the button numbering appears to be inconsistent between controllers when using two at the same time.
(Pressing the yellow button on one controller may give me a button number of 4, but 0 for the other controller).
Is this normal behaviour or is my code erroneous?
import pygame
pygame.init()
clock = pygame.time.Clock()
# Get the number of joysticks attached
joystick_count = pygame.joystick.get_count()
# Check for events
while True:
for event in pygame.event.get():
if event.type == pygame.JOYBUTTONDOWN:
print("Joystick button pressed.")
if event.type == pygame.JOYBUTTONUP:
print("Joystick button released.")
# For each joystick:
for j in range(joystick_count):
joystick = pygame.joystick.Joystick(j)
joystick.init()
buttons = joystick.get_numbuttons()
for i in range( buttons ):
button = joystick.get_button( i )
if button != 0:
print("Joystick {:1} Button {:>2} value: {}".format(j,i,button) )
clock.tick(20)
One controller was set to DirectInput (which works well with my pygame), whereas the other was set to Xinput.
When they are both set to DirectInput, button mapping is always consistent!

pygame game not starting on key press

I am making a game that shows game over on screen at the end of the game and if the player presses any key, the game starts again.
Game over screen is displayed but the problem is that when I am pressing any key, the game is not starting. I suspect function gameOverScreen() is not returning out of while loop, I could not understand why.
This function is called when the game is over, and it's continuously running until the player press any key:
def gameOverScreen():
textFont = pygame.font.Font('freesansbold.ttf',90)
while True:
overSurf = textFont.render('GAME OVER',True,RED)
overRect = overSurf.get_rect()
overRect.center = (WINDOWWIDTH/2,WINDOWHEIGHT/2)
DISPLAYSURF.blit(overSurf,overRect)
drawPressKeyMessage()
checkForKeyPress()
if checkForKeyPress():
pygame.event.get() #clear event queue
return
pygame.display.update()
FPSCLOCK.tick(FPS)
Function to check key press is:
def checkForKeyPress():
if len(pygame.event.get(QUIT)) > 0:
terminate()
keyUpEvents = pygame.event.get(KEYUP)
if len(keyUpEvents) == 0:
return None
else:
return keyUpEvents[0].key
Main function which call all functions is:
def main():
global FPSCLOCK, DISPLAYSURF, BASICFONT
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
pygame.display.set_caption('Wormy')
showStartScreen()
while True:
runGame()
showGameOverScreen()
Your keypress handler is called twice.
checkForKeyPress()
if checkForKeyPress():
The first call strips the event queue of kedown events so the second time it is called there is no keypress, so the loop cannot terminate.
There are two solutions:
either: remove the first call - take away the first of these lines:
checkForKeyPress() -- delete this line
if checkForKeyPress():
or: Store the result in a variable and use the variable in the if statement:
keyPress = checkForKeyPress()
if keyPress:
...

Pygame buttons not working

I have created some sort of menu navigation system in my game. All the screens are blitted in. The "Play" and "Quit" and "Controls" button works just fine but when I try to press menu from the controls screen, nothing happens. On the controls screen, you can faintly see the first menu screen from before. That might be the problem. I think that as the return to menu button is over the previous controls page button, it somehow is pressing the controls button from before. The button and menu segment of my code will be pasted here and the full thing will be pasted in a pastebin.
def text_to_button(msg,color,buttonx,buttony,buttonwidth,buttonheight,size = "small"):
textSurf, textRect = text_objects(msg,color,size)
textRect.center = ((buttonx + buttonwidth/2)), buttony+(buttonheight/2)
gameDisplay.blit(textSurf, textRect)
def button(text,x,y,width,height,inactive_color,active_color,size = "small",action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
#print(click)
if x + width > cur[0] > x and y + height > cur[1] > y:
pygame.draw.rect(gameDisplay, active_color,(x,y,width,height))
if click[0] == 1 and action != None:
if action == "quit":
pygame.quit()
quit()
if action == "controls":
game_controls()
if action == "play":
gameLoop()
if action == "main":
game_intro()
else:
pygame.draw.rect(gameDisplay, inactive_color,(x,y,width,height))
text_to_button(text,black,x,y,width,height,size)
def game_controls():
gcont = True
while gcont:
gameDisplay.blit(cont,(0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
button("Play",150,500,100,50,white,gray,"small",action = "play")
button("Main Menu",320,500,150,50,white,gray,"tiny", action = "main")
button("Quit",550,500,100,50,white,gray,"small", action = "quit")
pygame.display.update()
clock.tick(15)
def game_intro():
intro = True
while intro:
gameDisplay.blit(imggg,(0,0))
button("Play",150,500,100,50,white,gray,"small",action = "play")
button("ControLs",320,500,150,50,white,gray,"tiny", action = "controls")
button("Quit",550,500,100,50,white,gray,"small", action = "quit")
pygame.display.update()
clock.tick(15)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
intro = False
Full Code: https://pastebin.com/jrd82gkJ
You will have very hard time to debug your code in order to achieve the behavior you want for one simple reason:
The logic you use to switch between different screens providing different functionality is causing much trouble you can't directly see if you only run the game.
So you think: "oh ... how come the button doesn't work, there must be an issue with the button".
You are probably not aware of the fact that using functions having own while loops you go deeper and deeper into recursive calls with increasing recursion depth with each switch from one view to another - it is not how pygame is thought to be programmed.
I suggest you add some print() commands into your code to see in the console output that the code doesn't really do what you expect even if it appears to be OK at the first glance because it works.
Then I suggest you REWRITE your entire code so that you have one main while notGameExit: loop, and don't use any other looping in the helper functions. If you want use looping in your helper functions at least don't call from the helper functions another functions with own loops (and so on), but RETURN from them with an explicit return to avoid recursion.
If you leave the in the main loop called function with return your main loop will continue running and depending on some switches you can display in it different things on the screen and react differently to user actions.
Maybe looking at a minimal working pygame script showing "action" without usage of a loop you will gain better understanding and some deep "enlightenment" about how pygame works and then start a total rewrite of your game using another approach as this one you have used in the current code? Then come back with what you have achieved if you have further questions, but you won't probably have any, because it would be much easier to debug it yourself if the code will become more straightforward.
import pygame
pygame.init() # start PyGame (necessary because 'import pygame' doesn't start PyGame)
winDisplay = pygame.display.set_mode((1024, 768)) # set PyGame window size to 1024x768 pixel
pygame.display.set_caption("Minimal PyGame Test Script")
# Time in pygame is measured in milliseconds (1/1000 seconds) (defined by TIMER_RESOLUTION constant):
pygame.TIMER_RESOLUTION = 1000 # assure 1000 explicit, don't relay on default value
colorWhite = (255, 255, 255) # RGB color in Pygame format (valueRed=255, valueGreen=255, valueBlue=255)
colorRed = (255, 0, 0)
colorGreen = ( 0, 255, 0)
colorBlue = ( 0, 0, 255)
winDisplay.fill(colorWhite)
pygame.display.update()
pygame.time.wait(3000) # show the Pygame window for 3 seconds
winDisplay.fill(colorRed)
pygame.display.update()
pygame.time.wait(3000) # show the Pygame window for 3 seconds
winDisplay.fill(colorGreen)
pygame.display.update()
pygame.time.wait(3000) # show the Pygame window for 3 seconds
winDisplay.fill(colorBlue)
pygame.display.update()
pygame.time.wait(3000) # show the Pygame window for 3 seconds
winDisplay.fill(colorWhite)
pygame.display.update()
pygame.time.wait(3000) # show the Pygame window for 3 seconds

Pygame rect button stuck on screen, and close window code not working

I'm currently programming a game in Python 3.6 using Pygame.
My game has a start menu, with a button that is highlighted when hovered over.
When the button is clicked, the start menu disappears and the game starts.
The only problem is, the start button remains... I've tried using boolean values and if statements to make it disappear when the menu does, but to no avail.
To make matters worse, the pygame code that allows the window to be closed doesn't work (highlighted by commenting out). Could anyone help me with these small problems? They seem trivial, but I'm new to pygame and I can't seem to find a fix anywhere.
You will need the pictures below to run the program:
This is the photo of the girl.
This is the background photo.
The code:
#Imports
import sys
import pygame
pygame.init() #Initialise Pygame
#RGB colours
GREY = (128,128,128)
WHITE = (255,255,255)
BLACK = (0,0,0)
#fonts
title_font = pygame.font.Font('freesansbold.ttf',72)
menu_font = pygame.font.Font('freesansbold.ttf',32)
#images
girl_img = pygame.image.load('emily.png')
background_img = pygame.image.load('tech_lab.png')
class Game: #Game Class
def __init__(self): #Constructor
self.size = width, height = (1000,563)
self.screen = pygame.display.set_mode(self.size)
def menu_screen(self): #Menu Screen Function
intro = True
while intro:
## for event in pygame.event.get(): #To close window
## print(event)
## if event.type == pygame.QUIT:
## pygame.quit()
## sys.exit()
self.screen.fill(GREY) #Set background colour
self.screen.blit(title_font.render('EXAMPLE', True, BLACK), (330,100)) #Display game title
start_button = pygame.draw.rect(self.screen, (BLACK), pygame.Rect(410,310,180,55)) #Display start button rect
self.screen.blit(menu_font.render('Play', True, GREY), (425,310)) #Display start button text
pygame.display.flip() #Update display
while True:
pygame.event.get() #Get pygame events
click_pos = pygame.mouse.get_pos() #Get mouse position
if 410+180 > click_pos[0] > 410 and 310+55 > click_pos[1] > 310: #If mouse position within start button rect...
pygame.draw.rect(self.screen, (WHITE), pygame.Rect(410,310,180,55)) #then change colour of start button rect
if (pygame.mouse.get_pressed())[0] == 1: #If start button rect clicked...
start_game(self) #Start main game
else:
pygame.draw.rect(self.screen, (BLACK), pygame.Rect(410,310,180,55)) #Otherwise start button rect colour is black
self.screen.blit(menu_font.render('Play', True, GREY), (425,310))#Display start button text
pygame.display.flip() #Update display
def start_game(game): #Main game function
game.screen.blit(background_img, [0,0]) #Display background image
game.screen.blit(girl_img, [80,25]) #Display image
pygame.display.flip() #Update display
def main():
game = Game() #Instantiate class 'game'
game.menu_screen() #Call menu screen function
main()
This loop within a loop thing is going to cause nothing but problems and has to go:
while intro:
....
while True
....
here is some pseudo code:
Class Game:
def menu_screen():
displaying = True
while displaying:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# evaluate mouse click
if clicked button
displaying = False
self.game()
# drawing code for your images
pygame.display.flip()
def game():
game_running = True
while game_running:
# game running logic and drawing
def main():
# get the Game setup and call main menu
Basically, have your Game class control everything. it will have a menu function which displays the menu. Then they click the button and it goes to a different function within the game class for the actual game.
The start button was remaining because you never cleared the screen when going into the start_game() function. As far as why the close window button wasn't working, it is because of the loop within a loop as i mentioned above. You were trapped in the inner
while True:
loop and you could never get back to the event checking from above. (The quit code itself is fine, although you don't need pygame.quit() because you are quitting the program entirely with sys.ext())
Short of completely reworking your code, this can get you started on the right track.

Categories

Resources