Edit: added a longer example code.
I'm having trouble with coding buttons in pygame. I'm a newbie to the pygame module, so be gentle.
Basically, the goal is to make a point-and-click telltale kind of game. The player gets presented with two choices each gameloop, ie "go left" or "go right". Accordingly, there are two buttons in each gameloop, all located at the same coordinates.
Here is the function:
import pygame
import os
import time
pygame.init()
display_width= 1280
display_height = 720
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
def button(msg,x, y, w, h, ic, ac, action=None): #message, x y location, width, height, inactive and active colour
if action ==None:
pygame.display.update()
clock.tick(15)
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, ac,(x,y,w,h))
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
pygame.display.update()
clock.tick(15)
if action == "left1":
game_loop("loop1-1.png",0,0,"left2","left2","","")
else:
pygame.draw.rect(gameDisplay, ic,(x,y,w,h))
smallText = pygame.font.SysFont('timesnewroman',20)
textSurf, textRect = text_objects(msg, smallText, silver)
textRect.center = ( (x+(w/2)), (y+(h/2)) )
gameDisplay.blit(textSurf, textRect)
def game_loop(pic,width,heigth,act1,act2,left,right):
intro = True
while intro:
for event in pygame.event.get():
print(event)
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
gameDisplay.blit(get_image(pic), (0, 0)) #MAIN MENU PIC
button(left,440,450,width,heigth, dark_gray, gray, action=act1)#start nupp
button(right,740,450,width,heigth, dark_gray, gray, action=act2)#exit nupp
pygame.display.update()
clock.tick(15)
The problem occurs when I click the button carelessly, meaning if I don't purposefully click on the left mouse button as fast as I can. Once game_loop1 is called and if I click for a bit longer, the program will read the first click again in this game_loop1 and run the next game_loop and then the next etc. This means the player can accidentally skip through gameloops.
Is there a way to delay the program after the first click(however long it is)? Or maybe a way to include keyup in the function, so it won't count the click in the next gameloop?
Thanks!
I think your original code is a bit too convoluted to fix it and I'll rather show you some better ways to do what you want. You need a finite-state machine to transition between different states/scenes. You can find a simple example with functions as scenes here.
If the logic in your scenes is mostly the same, you could also try to just swap out the data for the scene, for example the background image. Each state/scene needs to know to which new states it can switch, so I put the data into a dictionary of dictionaries. The nested dicts contain the background image of the scene and the connected left and right scenes. When the user presses a button/rect I check if it was the left or right button and then switch to the corresponding scene (subdict) in the states dictionary.
import pygame
pygame.init()
display_width= 1280
display_height = 720
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
# Use uppercase names for constants that should never be changed.
DARK_GRAY = pygame.Color('gray13')
BACKGROUND1 = pygame.Surface((display_width, display_height))
BACKGROUND1.fill((30, 150, 90))
BACKGROUND2 = pygame.Surface((display_width, display_height))
BACKGROUND2.fill((140, 50, 0))
BACKGROUND3 = pygame.Surface((display_width, display_height))
BACKGROUND3.fill((0, 80, 170))
states = {
'scene1': {'background': BACKGROUND1, 'left_scene': 'scene2', 'right_scene': 'scene3'},
'scene2': {'background': BACKGROUND2, 'left_scene': 'scene1', 'right_scene': 'scene3'},
'scene3': {'background': BACKGROUND3, 'left_scene': 'scene1', 'right_scene': 'scene2'},
}
def game_loop():
# The buttons are just pygame.Rects.
left_button = pygame.Rect(440, 450, 60, 40)
right_button = pygame.Rect(740, 450, 60, 40)
# The current_scene is a dictionary with the relevant data.
current_scene = states['scene1']
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.MOUSEBUTTONDOWN:
# If the left button is clicked we switch to the 'left_scene'
# in the `current_scene` dictionary.
if left_button.collidepoint(event.pos):
current_scene = states[current_scene['left_scene']]
print(current_scene)
# If the right button is clicked we switch to the 'right_scene'.
elif right_button.collidepoint(event.pos):
current_scene = states[current_scene['right_scene']]
print(current_scene)
# Blit the current background.
gameDisplay.blit(current_scene['background'], (0, 0))
# Always draw the button rects.
pygame.draw.rect(gameDisplay, DARK_GRAY, left_button)
pygame.draw.rect(gameDisplay, DARK_GRAY, right_button)
pygame.display.update()
clock.tick(30) # 30 FPS feels more responsive.
game_loop()
pygame.quit()
Related
I am making a text based RPG as my first python/pygame project. In my game I would like to present the player with choices then ask them to type it in. I was able to download and import a module which accepts user input and displays it on the screen. However, before using it for anything like..lets say, if the user input is 'yes', then they go to a new area, I'd like the program to only accept the user input once they hit the enter key. I believe the tutorial for the textinput module I downloaded has directions to do this, but to be honest I just don't understand what it's saying. I've tried multiple types of loops but nothing is happening. Any help will be appreciated. Here is my main game code:
import pygame_textinput
import pygame
pygame.init()
#fps
clock=pygame.time.Clock()
# create font here
font_name = pygame.font.get_default_font()
WHITE_TEXT_COLOR = (255, 255, 255)
# create screen and window and display and font here
screen_width, screen_height = 800, 700
background_color_black = (0, 0, 0)
screen = pygame.display.set_mode((screen_width, screen_height))
our_game_display = pygame.Surface((screen_width, screen_height))
pygame.display.set_caption('MyRPGGame')
#create text input object
textinput = pygame_textinput.TextInput()
def draw_text(text, size, x, y):
pygame.font.init()
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, WHITE_TEXT_COLOR)
text_rect = text_surface.get_rect()
text_rect.center = (x, y)
our_game_display.blit(text_surface, text_rect)
def choose_to_play():
draw_text("You've decided to play",20,screen_width/2,screen_height/2+50)
def first_area():
our_game_display.fill(background_color_black)
draw_text('The story of this game depends on your choices. Do you wish to play?', 20, screen_width / 2,screen_height / 2 - 100)
draw_text('Type your answer and hit enter.', 20, screen_width / 2,screen_height / 2 - 50)
draw_text('Yes', 20, screen_width/2,screen_height/2+50)
draw_text('No', 20, screen_width/2,screen_height/2+100)
screen.blit(our_game_display, (0, 0))
pygame.display.update()
while True:
our_game_display.fill((background_color_black))
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
first_area()
# Feed it with events every frame
textinput.update(events)
# Blit its surface onto the screen
screen.blit(textinput.get_surface(), (10, 600))
pygame.display.update()
clock.tick(30)
Now here is the source for the pygame textinput module, figured I would link to the code so this post isn't too crowded:
https://github.com/Nearoo/pygame-text-input
You need to a variable for the state of the game. Once enter is pressed change the state. Implement different cases in the application loop depending on the state of the game::
game_state = 'start'
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
game_state = 'input'
our_game_display.fill((background_color_black))
if game_state == 'input':
textinput.update(events)
# [...]
I made an endscreen when ever my player health reaches -1 it loads the end screen but here is the problem Video it wont restart the game and I have to hold the start game to show my main game
my end screen def my point is how could I make it restart my game to the start not just blit the game where I was
#------------------------------------------------------
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def endScreen():
red = (200,0,0)
green = (0,200,0)
bright_red = (255,0,0)
bright_green = (0,255,0)
intro = True
while intro:
for event in pygame.event.get():
#print(event)
if event.type == pygame.QUIT:
pygame.quit()
quit()
window.fill((255,255,255))
largeText = pygame.font.Font('BLOODY.ttf',81)
TextSurf, TextRect = text_objects("You Have Died Fool", largeText)
TextRect.center = ((800/2), (800/2))
window.blit(TextSurf, TextRect)
button("Start Game",150,450,100,50,green,bright_green,game_loop)
# make the square brighter if collideded with the buttons
mouse = pygame.mouse.get_pos()
if 150+120 > mouse[0] > 150 and 450+50 > mouse[1] > 450:
pygame.draw.rect(window, bright_green,(150,450,120,50))
else:
pygame.draw.rect(window, green,(150,450,120,50))
if 550+110 > mouse[0] > 550 and 450+50 > mouse[1] > 450:
pygame.draw.rect(window, bright_red,(550,450,110,50))
else:
pygame.draw.rect(window, red,(550,450,110,50))
# ---------------------------------------------------------------------
smallText = pygame.font.Font("freesansbold.ttf",20)
textSurf, textRect = text_objects("Start Game", smallText)
textRect.center = ( (150+(120/2)), (450+(50/2)) )
window.blit(textSurf, textRect)
smallText = pygame.font.Font("freesansbold.ttf",20)
textSurf, textRect = text_objects("Quit Game", smallText)
textRect.center = ( (150+(910/2)), (450+(50/2)) )
window.blit(textSurf, textRect)
pygame.display.update()
clock.tick(15)
#----------------------------------------------------------
here is what I did in my main loop
when my player health reaches 0 it shows the end screen
def game_loop():
# coin scoring
font = pygame.font.Font('times.ttf',29)
score = 0
text = font.render("Hearts = " + str(score), True, (255,255,255))
textRect = text.get_rect()
textRect.center = ((80,70))
shootsright = []
bullets = []
bulls = []
bullss = []
bullsss = []
bullssss = []
runninggame = True
while runninggame:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runninggame = False
if playerman.health < -5:
endScreen()
The problem is that all of your game loop variables have been saved so when you restart you still have all the variables set to what they when you died.
So what you will need to do is put everything in one giant while loop. For example:
import ...
quit = False
while not quit:
# all of your code goes here
But once you do this you must change the pygame.QUIT if statement in your game loop as follows:
if event.type == pygame.QUIT:
runninggame = False
|
V
if event.type == pygame.QUIT:
sys.exit()
and the same goes for the pygame.QUIT at the top of your endScreen function.
Change 1: The next thing that must be changed is exiting the game loop function. This can be done by modifying one of the button functions to return a bool stating whether or not the restart button was clicked. From there that value can be passed onto the game loop function and if the button is pressed the while loop in the function will be broken out of causing everything in the while loop to rerun.
Explanation: Currently, the problem is that you never exit the loop meaning when you click the retry button you just start right back from where you let off. Instead, you need to redefine all your variables which is why all the code needs to be in a while loop (you can actually leave out the functions all that needs to be in the while loop are the global variables, but it is not necessary to move the functions out).
Change 2 (minor changes): Sys needs to be imported at the top of the script and the pygame.quit() function at the end of the script needs to be deleted.
Explanation: Sys is imported so that sys.exit can be run which allows the program to be terminated once the window is closed out. The pygame.quit() is deleted because running that will uninitialized pygame and that would prevent you from using any pygame functions after you have pressed the retry button. And this code is no longer necessary since sys.exit() pretty much just takes care of all that for you.
And a minor note: enemys should actually be enemies (it would look bad if you had the word enemies spelled incorrectly in your code and you put it up on GitHub) so I recommend just doing a quick replace all.
I'm having some trouble while trying to quit from a function that I made. It just doesn't seem to break the loop. The game opens and I can play around but I can't quit, It just stays there without doing anything and the icon on the task bar goes full yellow.
Here's my code:
import pygame, os, sys, math
black = (0,0,0)
white = (255,255,255)
grey = (128, 128, 128)
gameDisplay = pygame.display.set_mode((800,600))
def game_menu():
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
pygame.display.set_caption(".")
menu = True
events = pygame.event.get()
while menu:
for event in events:
if event.type == pygame.QUIT:
menu = False
pygame.quit()
quit()
DISPLAYSURF = pygame.display.set_mode((800, 600))
DISPLAYSURF.fill(black)
font = pygame.font.Font('MATRIX.ttf',60)
TextSurf, TextRect = text_objects("MATRIX PASA PALABRA", font,white)
TextRect.center = ((600/2),(50))
gameDisplay.blit(TextSurf, TextRect)
#Jugar
button("Jugar",300,200,200,50,None)
button("Instrucciones",300,275,200,50,None)
button("Dificultad",300,350,200,50,None)
button("Salir",300,425,200,50,None)
pygame.display.update()
def text_objects(text, font,color):
textSurface = font.render(text, True, color)
return textSurface, textSurface.get_rect()
def button(msg,x,y,w,h,action=None):
mouse = pygame.mouse.get_pos()
events = pygame.event.get()
if x+w> mouse[0] > (x) and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay,grey,(x,y,w,h))
for event in events:
if event.type ==pygame.MOUSEBUTTONUP and msg=="Salir":
pygame.quit()
quit()
elif event.type==pygame.MOUSEBUTTONUP and msg=="Jugar":
None
else:
pygame.draw.rect(gameDisplay,white,(x,y,w,h))
smalltext= pygame.font.Font("MATRIX.ttf",30)
textsrf,textrct=text_objects(msg,smalltext,black)
textrct.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(textsrf,textrct)
if __name__ == "__main__":
game_menu()
Thanks and sorry for my bad english.
The reason why it doesn't work is that you call pygame.event.get multiple times per frame. The event queue will be emptied after the first call, so the events won't get processed correctly. There should be only one pygame.event.get call per frame.
To fix this problem, you can assign the list of events that pygame.event.get returns to a variable in the main while loop and then pass it to the button function.
while menu:
events = pygame.event.get()
for event in events:
# etc.
button("Jugar",300,200,200,50,events,None)
Add an events parameter to the button function:
def button(msg,x,y,w,h,events,action=None):
mouse = pygame.mouse.get_pos()
if x+w> mouse[0] > (x) and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay,grey,(x,y,w,h))
for event in events:
# etc.
BTW, this button function keeps popping up here again and again, probably because it's a really bad way to implement buttons. I'd recommend a solution similiar to one of these: https://stackoverflow.com/a/47664205/6220679 or search for pygame GUI toolkits (SGC is pretty good).
The others have already mentioned that the indentation in your example is wrong and that sys.exit is a better way to quit than the quit function.
Here's a fixed, complete example:
import pygame, os, sys, math
black = (0,0,0)
white = (255,255,255)
grey = (128, 128, 128)
gameDisplay = pygame.display.set_mode((800,600))
def game_menu():
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
pygame.display.set_caption(".")
DISPLAYSURF = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
menu = True
while menu:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
menu = False
pygame.quit()
sys.exit()
DISPLAYSURF.fill((30, 30, 30))
font = pygame.font.Font(None,60)
TextSurf, TextRect = text_objects("MATRIX PASA PALABRA", font,white)
TextRect.center = ((600/2),(50))
gameDisplay.blit(TextSurf, TextRect)
#Jugar
button("Jugar",300,200,200,50,events,None)
button("Instrucciones",300,275,200,50,events,None)
button("Dificultad",300,350,200,50,events,None)
button("Salir",300,425,200,50,events,None)
pygame.display.update()
clock.tick(60)
def text_objects(text, font,color):
textSurface = font.render(text, True, color)
return textSurface, textSurface.get_rect()
def button(msg,x,y,w,h,events,action=None):
mouse = pygame.mouse.get_pos()
if x+w> mouse[0] > (x) and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay,grey,(x,y,w,h))
for event in events:
if event.type ==pygame.MOUSEBUTTONUP and msg=="Salir":
pygame.quit()
sys.exit()
elif event.type==pygame.MOUSEBUTTONUP and msg=="Jugar":
print("jugar")
else:
pygame.draw.rect(gameDisplay,white,(x,y,w,h))
smalltext= pygame.font.Font(None,30)
textsrf,textrct=text_objects(msg,smalltext,black)
textrct.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(textsrf,textrct)
if __name__ == "__main__":
game_menu()
If you import sys then you can use that to exit your code.
import sys
def game_menu():
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
Try adding pygame.display.quit() before the line pygame.quit(). This should close any open displays.
Edit:
The problem is that most of your program isn't inside the while loop. Most importantly, the events = pygame.event.get() isn't inside the while loop so the events are never updated.
Rearranging the code to something like this should work:
def game_menu():
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
pygame.display.set_caption(".")
menu = True
while menu:
events = pygame.event.get()
DISPLAYSURF = pygame.display.set_mode((800, 600))
DISPLAYSURF.fill(black)
font = pygame.font.Font('MATRIX.ttf',60)
TextSurf, TextRect = text_objects("MATRIX PASA PALABRA", font,white)
TextRect.center = ((600/2),(50))
gameDisplay.blit(TextSurf, TextRect)
#Jugar
button("Jugar",300,200,200,50,None)
button("Instrucciones",300,275,200,50,None)
button("Dificultad",300,350,200,50,None)
button("Salir",300,425,200,50,None)
pygame.display.update()
for event in events:
if event.type == pygame.QUIT:
menu = False
pygame.quit()
quit()
So I'm trying to make this button change color when I hover over it, but pygame.mouse.get_pos() is not updating after I open the program.
I'm a novice to both python and program so any assistance would be a appreciated greatly.
import pygame
pygame.init()
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption('Click to Adventure')
clock = pygame.time.Clock()
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
green = (0,255,0)
gameDisplay.fill(white)
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def button(msg,x,y,w,h,ic,ac,action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
ycrd = int((y+(h/2)))
r = int(h/2)
print(mouse)
if x+(w+(h/2)) > mouse[0] > x-(h/2) and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay,ac,(x,y,w,h))
pygame.draw.circle(gameDisplay,ac,(x,ycrd),r,0)
pygame.draw.circle(gameDisplay,ac,(x+w,ycrd),r,0)
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay,ic,(x,y,w,h))
pygame.draw.circle(gameDisplay,ic,(x,ycrd),r,0)
pygame.draw.circle(gameDisplay,ic,(x+w,ycrd),r,0)
smallText = pygame.font.SysFont("comicsansms",20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(textSurf, textRect)
button("Hi",300,200,100,50,red,green,None)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
#print(event)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
I'm not really sure why pygame.mouse.get_pos() isn't updating as I have both pygame.time.Clock() and clock.tick(60).
I'm not a pygame expert myself but what i can see is that you're calling the button() function only once when the game is starting. You need to make a function that will be called in every frame which will wait for an event to happen, in your case, changing the button's color.
Make a normal button on the screen. Then, make a function which will be called when the mouse is on top of the button and change color.
Something like,
Button = MakeButton(); #This function will create a button and show on the screen
In while not gameExit:
if Button.get_rect().collidepoint(pygame.mouse.get_pos()): #This line will wait for the mouse to hover over the button
ChangeButtonColor(); #This function will change the button color
These are pseudo codes to get you started.
pygame uses the "update/draw loop" pattern, commonly found in game development frameworks. This pattern consists of a loop that is mostly non-terminating, and you have it in your code as while not gameExit:. Inside this loop, you must handle all of the user input, through the usage of event flags. For example, your code deals with the QUIT event.
However, your button code is not particularly tailored for this pattern. Instead, it hopes to create a "button" as some sort of static object on the screen, like event-based GUIs.
First, you should separate the update step and the draw step. The update changes the game state, and the draw step simply checks the state and draw things on screen. In your case, the update step should check if the mouse is over the button or not, and define which color is used.
Let's do that separation:
# to be used in the draw step
def draw_button(msg, x, y, width, height, color):
ycrd = int((y + (h / 2)))
r = int(h / 2)
pygame.draw.rect(gameDisplay, color, (x, y, width, height))
pygame.draw.circle(gameDisplay, color, (x, ycrd), r, 0)
pygame.draw.circle(gameDisplay, color, (x + w, ycrd), r, 0)
smallText = pygame.font.SysFont("comicsansms",20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ((x + (w / 2)), (y + (h / 2)))
# to be used in the update step
def check_mouse_state_over_box(x, y, width, height):
# I'm returning strings here, but you should use something else like an enum
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + (w + (h / 2)) > mouse[0] > x - (h / 2) and y + h > mouse[1] > y:
if click[0] == 1:
return 'click'
else:
return 'hover'
else:
return 'no-relation'
Now that the button() function separates the update and draw steps, you can properly use it in the game loop:
while not game_exit:
# UPDATE STEP
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit() # this function is undefined it seems?
# you can also use 'elif event.type == pygame.MOUSEMOTION:' if you wish, but then the logic is a bit different
button_state = check_mouse_over_box(300, 200, 100, 50)
if button_state = 'no-relation':
button_color = red
else:
button_color = green
if button_state = 'click':
action() # define whatever action you want
# DRAW STEP
draw_button('Hi', 300, 200, 100, 50, button_color)
gameDisplay.blit() # I think this is mandatory in pygame
Now, this should work. But if you know object-oriented programming (OOP), you could define a class Button that holds its position and color, an update method that checks the mouse and change the button state properly, and a draw method that draws the button. If you don't know OOP, this is a great opportunity to learn it :)
I have no idea why when the button is pressed it doesnt just go to the next function. It goes back to the other one its really weird and annoying.
It should go to the whattodo function but it does for like half a second then goes to the other. Anyone know why?
#!/usr/bin/python
import pygame
pygame.init()
screen = pygame.display.set_mode((1400,700))
pygame.display.set_caption("Anti-Docter")
titlescreen = pygame.image.load('titleframe.bmp')
boxinfo = pygame.image.load('boxinfo.bmp')
black = (0,0,0)
white = (255,255,255)
randommommycolor = (255,139,12)
Brown = (102,51,0)
Lighter_Brown = (120,59,5)
def mts(text, textcolor, x, y, fs):
font = pygame.font.Font(None,fs)
text = font.render(text, True, textcolor)
screen.blit(text, [x,y])
def buttonPlay(x,y,w,h,ic,ac):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(screen, ac,(x,y,w,h))
if click[0] == 1:
whattodo()
else:
pygame.draw.rect(screen, ic,(x,y,w,h))
def whattodo():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.blit(boxinfo, (0,0))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.blit(titlescreen, (0,0))
buttonPlay(580, 350, 200, 90, Brown, Lighter_Brown)
mts("Play", black, 620, 365, 80)
pygame.display.update()
Every time your main loop repeats, it calls 'buttonPlay'. There is nothing inside 'whattodo' that changes this behaviour, so 'whattodo' runs once then control flow passes back to the main loop, which just repeats 'buttonPlay'.