Buttons and text flickering - python

I copied most of this code from an earlier script which worked fine, but when I load up the game, pretty much all the elements are flickering.
Here is the Minimal complete verifiable example here:
import pygame
pygame.init()
displayWidth = 1280
displayHeight = 720
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
pygame.display.set_caption('Grow')
black = (0, 0, 0)
brown = (100, 100, 0)
green = (0, 200, 0)
lightGreen = (0, 255, 0)
red = (200, 0, 0)
lightRed = (255, 0, 0)
smallfont = pygame.font.SysFont("comicsansms", 25)
medfont = pygame.font.SysFont("comicsansms", 50)
largefont = pygame.font.SysFont("comicsansms", 80)
clock = pygame.time.Clock()
FPS = 60
def gameIntro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(brown)
message_to_screen('Welcome to Tanks', green, -100, 'large')
message_to_screen('The objective is to shoot and destroy', black, -30)
message_to_screen('Shoot and destroy the enemy tank before they destroy you', black, 10)
message_to_screen('The more enemies you destroy, the harder they get', black, 50)
button('quit', 500, 500, 100, 50, red, lightRed, action = 'quit')
button('play', 200, 500, 100, 50, green, lightGreen, action = 'play')
pygame.display.update()
clock.tick(0)
def gameLoop():
pygame.quit()
quit()
def button(text, x, y, width, height, inactive_color, active_color, action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
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 == 'play':
gameLoop()
else:
pygame.draw.rect(gameDisplay, inactive_color, (x, y, width, height))
text_to_button(text, black, x, y, width, height)
def text_to_button(msg, colour, buttonx, buttony, buttonwidth, buttonheight, size = 'small'):
textSurf, textRect = text_objects (msg, colour, size)
textRect.center = ((buttonx + (buttonwidth/2)), buttony + (buttonheight/2))
gameDisplay.blit(textSurf, textRect)
pygame.display.update()
def message_to_screen(msg, colour, y_displace = 0, size = 'small'):
textSurf, textRect = text_objects (msg, colour, size)
textRect.center = (displayWidth / 2), (displayHeight / 2) + y_displace
gameDisplay.blit(textSurf, textRect)
pygame.display.update()
def text_objects(text, colour, size):
if size == 'small':
textSurface = smallfont.render (text, True, colour)
elif size == 'medium':
textSurface = medfont.render (text, True, colour)
elif size == 'large':
textSurface = largefont.render (text, True, colour)
return textSurface, textSurface.get_rect()
gameIntro()
For some reason, in the gameIntro function, when I call the message_to_screen and button functions, they all start flickering (everything except the first function called). Changing the order of the functions changes which one will stop flickering. Everything else works fine. The buttons change colour when the cursor is above them and they can still be clicked and have functionality

Don't call pygame.display.update() multiple times in each iteration of your game loop. It will lead to the screen flickering you see.
Simply remove the pygame.display.update() calls in the message_to_screen and text_to_button functions.

Related

Text Size Change Persistence

I'll start with simply copy and pasting my huge chunk of code:
# Libraries #
import pygame
from pygame.locals import *
import ResolutionMenu
# Classes #
class GuiSettings:
def __init__(self):
self.TextSize = 20
self.button_color = (35, 65, 145)
self.button_color_hover = (70, 105, 150)
class Button():
def __init__(self, x, y, width, height, outline, settings, text = "", action = None):
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.settings = settings
self.action = action
self.outline = outline
def draw(self, screen, outline = None):
if self.outline:
pygame.draw.rect(screen, self.outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
color = self.settings.button_color if not self.isOver(pygame.mouse.get_pos()) else self.settings.button_color_hover
pygame.draw.rect(screen, color, (self.x, self.y, self.width, self.height), 0)
if self.text != "":
font = pygame.font.SysFont('segoeuisemibold', self.settings.TextSize)
text = font.render(self.text, 1, (0, 0, 0))
screen.blit(text, (self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def update(self, events):
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN and self.isOver(pygame.mouse.get_pos()) and self.action:
self.action()
def isOver(self, pos):
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
# Variables #
settings = GuiSettings()
Click = False
Clock = pygame.time.Clock()
H, W = ResolutionMenu.resolution
pygame.init()
pygame.display.set_caption("Main Menu")
font = pygame.font.SysFont(None, settings.TextSize)
screen = pygame.display.set_mode((ResolutionMenu.resolution))
# Functions #
def draw_text_center(text, font, colour, surface):
textobj = font.render(text, 1, colour)
textrect = textobj.get_rect()
textrect.center = ((H / 2), (W / 10))
surface.blit(textobj, textrect)
def draw_text(text, font, colour, surface, XandY):
textobj = font.render(text, 1, colour)
textrect = textobj.get_rect()
textrect.topleft = (XandY)
surface.blit(textobj, textrect)
def MainMenu():
while True:
if ResolutionMenu.resolution == (800, 600):
screen.fill ((0, 0, 0))
draw_text_center("Main Menu Navigation", font, (255, 255, 255), screen)
mx, my = pygame.mouse.get_pos()
button1 = pygame.Rect((W/10), (H/5), (W/3), (H/15))
button2 = pygame.Rect((W/10), (H/2), (W/3), (H/15))
if button1.collidepoint((mx, my)):
if Click:
game()
if button2.collidepoint((mx, my)):
if Click:
OptionsMenu()
pygame.draw.rect(screen, (255, 0, 0), button1)
pygame.draw.rect(screen, (255, 0, 0,), button2)
Click = False
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
Click = True
pygame.display.update()
Clock.tick(60)
def game():
running = True
while running:
screen.fill((0,0,0))
draw_text('Test 1', font, (255, 255, 255), screen, (20, 20))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
pygame.display.update()
Clock.tick(60)
def OptionsMenu():
screen = pygame.display.set_mode((800, 600))
settings = GuiSettings()
buttons = [
Button(100, 150, 200, 50, (0, 0, 0), settings, "Set Text Size: 14", lambda: settings.__setattr__('TextSize', 14)),
Button(500, 150, 200, 50, (0, 0, 0), settings, "Set Text Size: 16", lambda: settings.__setattr__('TextSize', 16)),
Button(100, 300, 200, 50, (0, 0, 0), settings, "Set Text Size: 18", lambda: settings.__setattr__('TextSize', 18)),
Button(500, 300, 200, 50, (0, 0, 0), settings, "Set Text Size: 20", lambda: settings.__setattr__('TextSize', 20))
]
running = True
while running:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
return
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
for button in buttons:
button.update(events)
screen.fill((100, 100, 100))
for button in buttons:
button.draw(screen)
pygame.display.flip()
MainMenu()
So as you can see, recently I've been trying to make a main menu navigation panel for a game, and all is going well so far. I have stumbled into a problem, however.
The problem is thus: I have created a class of which holds the settings for text size, the button colour and the button hover colour (the colour of the button will change when the mouse pos is inside of the button's area).
class GuiSettings:
def __init__(self):
self.TextSize = 20
self.button_color = (35, 65, 145)
self.button_color_hover = (70, 105, 150)
This works completely fine when I'm inside of the OptionMenu() function, but when breaking that function, by pressing esc and returning to the MainMenu() function, the text size of the "Main Menu Navigation" text (Code below) doesn't change, despite using the 'settings.TextSize' as the size variable. I'm not sure why this is seeing as on the options menu uses a lambda function to change the value of the text size.
buttons = [
Button(100, 150, 200, 50, (0, 0, 0), settings, "Set Text Size: 14", lambda: settings.__setattr__('TextSize', 14)),
Button(500, 150, 200, 50, (0, 0, 0), settings, "Set Text Size: 16", lambda: settings.__setattr__('TextSize', 16)),
Button(100, 300, 200, 50, (0, 0, 0), settings, "Set Text Size: 18", lambda: settings.__setattr__('TextSize', 18)),
Button(500, 300, 200, 50, (0, 0, 0), settings, "Set Text Size: 20", lambda: settings.__setattr__('TextSize', 20))
]
Sorry that this has been such a long question, thank you for reading and I hope someone better at this than me can give me some help.
You create the font object before the main application loop with the font size settings.TextSize. This font object doesn't magically change when you change the settings.TextSize afterwards. The font size is used to rasterize the glyphs in the font. You need to create a new font object with a new size after the function OptionsMenu is left.
Furthermore you need to use the global statement, since font and settings are variables in global namespace.
def MainMenu():
global font # font is a variable in global namespace
while True:
if ResolutionMenu.resolution == (800, 600):
# [...]
if button2.collidepoint((mx, my)):
if Click:
OptionsMenu()
# create new font with new size
font = pygame.font.SysFont(None, settings.TextSize)
# [...]
def OptionsMenu():
global settings # settings is a variable in global namespace
screen = pygame.display.set_mode((800, 600))
# settings = GuiSettings() # <--- DELETE use the existing object instead
# [...]

"Stuttering" StartMenu for Pygame

I've been working on and off on a text-based adventure game for a few years now. It originally started as a way to learn Python, then I moved on to projects more relevant to my career after I became comfortable with the language. I'm now somewhat competent (Still a noob, but you know, progress) and I'd like to go back to the game to add in more complex functionality.
One thing that is annoying me is a visual bug in my start menu. Below is the code:
import pygame
from pygame_functions import setBackgroundImage
import gamefile
pygame.init()
display_width = 1500
display_height = 750
startMenu = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("The Woodsman's Tale")
black = (0, 0, 0)
green = (0, 200, 0)
white = (255, 255, 255)
dark_red = (200, 0, 0)
bright_green = (0, 255, 0)
leaf_green = (0, 175, 75)
brown = (102, 51, 0)
red = (255, 0, 0)
clock = pygame.time.Clock()
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()
if x + w > mouse[0] > x and y + h > mouse[1] > y:
pygame.draw.rect(startMenu, ac, (x, y, w, h))
if click[0] == 1 and action is not None:
if action == 'play':
gamefile.rungame()
start_menu().intro = False
elif action == 'quit':
pygame.quit()
quit()
else:
pygame.draw.rect(startMenu, ic, (x, y, w, h))
smallText = pygame.font.Font("freesansbold.ttf", 20)
TextSurf, TextRect = text_objects(msg, smallText)
TextRect.center = (x + (w / 2), (y + (h / 2)))
startMenu.blit(TextSurf, TextRect)
def start_menu():
intro = True
while intro:
setBackgroundImage('startScreen.png')
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
largeText = pygame.font.Font('BRADHITC.ttf', 90)
TextSurf, TextRect = text_objects("The Woodsman's Tale", largeText)
TextRect.center = (display_width / 2, display_height / 3)
startMenu.blit(TextSurf, TextRect)
button('Play', 350, 500, 200, 150, green, leaf_green, 'play')
button('Quit', 850, 500, 200, 150, dark_red, red, 'quit')
pygame.display.update()
clock.tick(15)
if __name__ == '__main__':
start_menu()
The setbackgroundimage function being imported is this:
def setBackgroundImage(img):
global bgSurface, backgroundImage
surf = loadImage(img)
backgroundImage = surf
screen.blit(surf, [0, 0])
bgSurface = screen.copy()
updateDisplay()
Now, the start menu works great. Everything functions properly. My issue is that when I click on the title bar, for example to move the window, the start menu begins stuttering visually.
Exactly what is happening is this: Once I click on the title bar, the "buttons" of the start menu and the Title text of the game disappear. Once the click is released, the buttons and Title text reappear, but stutter rapidly.
I'm not really sure if I'm providing enough for anyone to be able to tell what's going wrong, so I apologize if that's the case.
The fist issue is that setBackgroundImage seems to update the display (updateDisplay()).
The display has to be updated once at the end of the main application loop, and should not be updated multiple times. That cause the flickering. Remove the display update from setBackgroundImage:
def start_menu():
intro = True
while intro:
setBackgroundImage('startScreen.png') # draw background but do no update the display
# [...]
pygame.display.update() # the one and only update at the end of the loop
clock.tick(15)
The second issue, that the background image is loaded in setBackgroundImage. That cause that the image is loaded continuously in every frame. That causes a performance impact.
Load the image before the main loop and pass the Surface object to setBackgroundImage:
def start_menu():
surf = loadImage('startScreen.png')
intro = True
while intro:
setBackgroundImage(surf)
# [...]

pygame.quit() giving init.font error while switching files

I made a main python file that holds the whole game itself and for each window, I'm using different files. For instance, for the beginning of the game, I want to create a window where you can choose the language with 2 boxes, which makes a variable = 1 or 2. The main file reads the variable and with an if condition the language is defined.
The main problem is that when I want to close the language file and resume with the main one, it pops up an error saying the font is not defined when I did pygame.quit()
MAIN FILE
import pygame
from lib.pygame_functions import *
from pygame.locals import *
import time
import random
def language():
import lib.language.languageVS
from lib.language.languageVS import lang
language()
pygame.quit()
if lang == '1':
from languages.en import *
lang_selection = 1
print(selection_language)
elif lang == '2':
from languages.es import *
lang_selection = 1
print(selection_language)
pygame.init()
white = (255,255,255)
black = (0,0,0)
green = (0, 200, 0)
red = (200, 0, 0)
blue = (0, 0, 200)
bright_green = (0, 255, 0)
bright_red = (255, 0, 0)
bright_blue = (0, 0, 255)
introbackground = pygame.image.load('introbg.png')
gameDisplay = pygame.display.set_mode((800, 600)) #surface
pygame.display.set_caption('Neon Fight') #titutlo
def names():
import lib.playernames.TEXTINPUT
from lib.playernames.TEXTINPUT import a
print(a)
import lib.playernames.TEXTINPUT2
from lib.playernames.TEXTINPUT2 import b
print(b)
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font('freesansbold.ttf',115)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
pygame.display.update()
def quitgame():
pygame.quit()
quit()
def game():
time.sleep(4)
def startgame():
pygame.quit()
names()
game()
def button(msg, x, y, w, h, ic, ac, action=None):
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))
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
smallText = pygame.font.Font('freesansbold.ttf', 20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ((x+(w/2)), (y+(h/2)))
gameDisplay.blit(textSurf, textRect)
def game_intro():
intro = True
while intro:
gameDisplay.blit(introbackground, (0, 0))
#gameDisplay.fill(white)
mouse = pygame.mouse.get_pos()
#print(mouse)
#BUTTONS
button('Play', 350, 325, 100, 50, green, bright_green, startgame)
button('Instructions', 350, 400, 100, 50, blue, bright_blue)
button('Quit', 350, 475, 100, 50, red, bright_red, quitgame)
pygame.display.update()
#quit_x
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
pygame.quit() #desinizialization
quit()
game_intro()
gameExit = False
while not gameExit: #quit_x
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
gameDisplay.fill(white)
pygame.display.update() #actualizar_surface
pygame.quit() #desinizialization
quit()
lib.language.languageVS
import pygame
from pygame.locals import *
import time
import random
lang = 0
pygame.init() #inizialization
white = (255,255,255)
black = (0,0,0)
green = (0, 200, 0)
red = (200, 0, 0)
blue = (0, 0, 200)
bright_green = (0, 255, 0)
bright_red = (255, 0, 0)
bright_blue = (0, 0, 255)
gameDisplay = pygame.display.set_mode((800, 600)) #surface
pygame.display.set_caption('Neon Fight') #titutlo
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font('freesansbold.ttf',115)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
pygame.display.update()
def english():
global lang
lang = 1
pygame.quit()
def spanish():
global lang
lang = 2
pygame.quit()
def button(msg, x, y, w, h, ic, ac, action=None):
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))
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
smallText = pygame.font.Font('freesansbold.ttf', 20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ((x+(w/2)), (y+(h/2)))
gameDisplay.blit(textSurf, textRect)
def language():
language = True
while language:
gameDisplay.fill(white)
mouse = pygame.mouse.get_pos()
#print(mouse)
#BUTTONS
button('Play', 350, 325, 100, 50, green, bright_green, spanish)
button('Instructions', 350, 400, 100, 50, blue, bright_blue, english)
pygame.display.update()
#quit_x
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
pygame.quit() #desinizialization
quit()
language()
gameExit = False
while not gameExit: #quit_x
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
gameDisplay.fill(white)
pygame.display.update() #actualizar_surface
TRACEBACK:
Exception has occurred: error
font not initialized
File "F:\APPVISUAL\lib\language\languageVS.py", line 55, in button
smallText = pygame.font.Font('freesansbold.ttf', 20)
File "F:\APPVISUAL\lib\language\languageVS.py", line 73, in language
button('Play', 350, 325, 100, 50, green, bright_green, spanish)
File "F:\APPVISUAL\lib\language\languageVS.py", line 85, in <module>
language()
An easy solution would be to add pygame.init() before
smallText = pygame.font.Font('freesansbold.ttf', 20)
Managed to solve it. Apparently just changing the function worked. I thought that instead of using pygame.quit() to close the pygame module, i could just add everything on a loop and whenever an option is selected the loop stops going on and lets the code end by itself without forcing it. In that way i can import it in the main file and code keep running! This is what the code is in lib.languageVS file:
import pygame
from pygame.locals import *
import time
import random
import sys
lang = 0
pygame.init() #inizialization
white = (255,255,255)
black = (0,0,0)
green = (0, 200, 0)
red = (200, 0, 0)
blue = (0, 0, 200)
bright_green = (0, 255, 0)
bright_red = (255, 0, 0)
bright_blue = (0, 0, 255)
gameDisplay = pygame.display.set_mode((800, 600)) #surface
pygame.display.set_caption('Neon Fight') #titutlo
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font('freesansbold.ttf',115)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
pygame.display.update()
def english():
global lang
lang = 1
def spanish():
global lang
lang = 2
def button(msg, x, y, w, h, ic, ac, action=None):
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))
if click[0] == 1 and action != None:
action()
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
smallText = pygame.font.Font('freesansbold.ttf', 20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ((x+(w/2)), (y+(h/2)))
gameDisplay.blit(textSurf, textRect)
def language():
language = True
while language == True:
gameDisplay.fill(white)
mouse = pygame.mouse.get_pos()
#print(mouse)
#BUTTONS
button('SPANISH', 350, 325, 100, 50, green, bright_green, spanish)
button('ENGLISH', 350, 400, 100, 50, blue, bright_blue, english)
if lang == 1:
language = False
elif lang == 2:
language = False
pygame.display.update()
#quit_x
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
pygame.quit() #desinizialization
language()
##OLD_VERSION
# def language():
# language = True
# while language:
# gameDisplay.fill(white)
# mouse = pygame.mouse.get_pos()
# #print(mouse)
# #BUTTONS
# button('SPANISH', 350, 325, 100, 50, green, bright_green, spanish)
# button('ENGLISH', 350, 400, 100, 50, blue, bright_blue, english)
# pygame.display.update()
# #quit_x
# for event in pygame.event.get():
# if event.type == pygame.QUIT:
# gameExit = True
# pygame.quit() #desinizialization
# if lang == 1:
# pygame.quit()
# elif lang == 2:
# pygame.quit()

Need help creating buttons in pygame

I have been assigned a homework problem in pygame to make two rectangles, one red , one green and when clicked, text appears which gives the name of the colour of the button, I have created the shapes successfully, however I don't know how to make the text appear when the button is clicked. Any help is appreciated
I have successfully drawn two rectangles using the pygame.draw.rect() function and I have called pos = pygame.mouse.get_pos() in order to get an x and y value for the user's mouse.
pos = pygame.mouse.get_pos()
x = pos[0]
y = pos[1]
screen.fill(WHITE)
pygame.draw.rect(screen, GREEN, [100, 300, 200, 200])
pygame.draw.rect(screen, RED, [400, 300, 200, 200])
pygame.display.flip()
clock.tick(60)
The result is two rectangles, one green, one red in the centre of the screen.
To verify if amouse button is pressed oyu can use pygame.mouse.get_pressed():
leftClicked = pygame.mouse.get_pressed()[0]
For text rendering you can use pygame.font or pygame.pygame.freetype. e. g:
import pygame.freetype
font = pygame.freetype.SysFont('Times New Roman', 30)
Define a pygame.Rect for each button:
rect_green = pygame.Rect(100, 300, 200, 200)
rect_red = pygame.Rect(400, 300, 200, 200)
Verify if the mouse button is pressed an the mouse coursor is on the button by .collidepoint()). e.g.:
pygame.draw.rect(screen, GREEN, rectGreen)
if leftClicked and rectGreen.collidepoint(pos):
text_surf, text_rect = font.render("GREEN", WHITE, size=30)
text_rect.center = rectGreen.center
screen.blit(text_surf, text_rect)
See the short example:
import pygame
import pygame.freetype
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
pygame.init()
size = (800,600)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
font = pygame.freetype.SysFont('Times New Roman', 30)
rect_green = pygame.Rect(100, 300, 200, 200)
rect_red = pygame.Rect(400, 300, 200, 200)
def DrawButton(surf, cursor_pos, pressed, rect, color, text_color, text):
pygame.draw.rect(surf, color, rect)
if pressed and rect.collidepoint(cursor_pos):
text_surf, text_rect = font.render(text, text_color, size=30)
text_rect.center = rect.center
screen.blit(text_surf, text_rect)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pos = pygame.mouse.get_pos()
x = pos[0]
y = pos[1]
leftClicked = pygame.mouse.get_pressed()[0]
screen.fill(WHITE)
DrawButton(screen, pos, leftClicked, rect_green, GREEN, BLACK, "GREEN")
DrawButton(screen, pos, leftClicked, rect_red, RED, BLACK, "RED")
pygame.display.flip()
clock.tick(60)
pygame.quit()

Clicking buttons in pygame

So I have created a small menu that contains a "Start" button and a "quit" button. Obviously what I want is when I click Start, it starts the game loop, and when I hit quit, it quits the game. I already coded something, but it doesn't work. I think the problem is that I don't have a function for game loop and quit.
Here is the whole code:
import pygame
pygame.init()
win = pygame.display.set_mode((1200, 600))
pygame.display.set_caption("WALTHER")
clock = pygame.time.Clock()
BLACK = (0, 0, 0)
WHITE = (255,255,255)
RED = (255, 0, 0)
GREEN = (13, 255, 0)
YELLOW = (0, 255, 20)
BRIGHT_YELLOW = (255, 255, 20)
player = pygame.Rect(40, 45, 30, 30)
vel = 4
vel_left = 5
vel_right = -5
class MovingRect(pygame.Rect):
def __init__(self, x, y, w, h, vel):
# Call the __init__ method of the parent class.
super().__init__(x, y, w, h)
self.vel = vel
def update(self):
self.x += self.vel # Move.
if self.right > 600 or self.left < 320: # If it's not in this area.
self.vel = -self.vel # Invert the direction.
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()
if x + w > mouse[0] > x and y + h > mouse[1] > y:
pygame.draw.rect(win, ac, (x, y, w, h))
if click[0] == 1 and action != None:
if action == "Quit":
pygame.quit()
else:
pygame.draw.rect(win, ic, (x, y, w, h))
smallText = pygame.font.Font("freesansbold.ttf",50)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ((x+(w/2)), (y+(h/2)))
win.blit(textSurf, textRect)
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
win.fill(WHITE)
largeText2 = pygame.font.Font('freesansbold.ttf', 115)
TextSurf, TextRect = text_objects("Red Square", largeText2)
TextRect.center = ((600), (100))
win.blit(TextSurf, TextRect)
button("Start", 525, 250, 150, 60, YELLOW, BRIGHT_YELLOW)
button("Quit", 525, 350, 150, 60, YELLOW, BRIGHT_YELLOW)
pygame.display.update()
clock.tick(15)
def message_display(text):
largeText = pygame.font.Font(None , 115)
win.blit(largeText.render(text, True, (RED)), (370,250))
pygame.display.update()
pygame.time.wait(2000)
pygame.quit()
rotatingrects = [
pygame.Rect(630, 300, 250, 20),
pygame.Rect(920, 300, 250, 20)
]
movingrects = [
MovingRect(320, 120, 30, 30, vel_left),
MovingRect(320, 240, 30, 30, vel_left),
MovingRect(320, 360, 30, 30, vel_left),
MovingRect(570, 180, 30, 30, vel_right),
MovingRect(570, 300, 30, 30, vel_right),
MovingRect(570, 420, 30, 30, vel_right)
]
walls = [
pygame.Rect(0, 0, 1200, 20), pygame.Rect(0, 0, 20, 600),
pygame.Rect(0, 580, 1200, 20), pygame.Rect(1180, 0, 20, 600),
pygame.Rect(300, 0, 20, 530), pygame.Rect(20, 100, 230, 20),
pygame.Rect(70, 200, 230, 20), pygame.Rect(20, 300, 230, 20),
pygame.Rect(70, 400, 230, 20), pygame.Rect(600, 100, 20, 500)
]
run = True
while run:
# Handle the events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
game_intro()
keys = pygame.key.get_pressed()
# Update the player coordinates.
if keys[pygame.K_LEFT] and player.x > 0:
player.x -= vel
if keys[pygame.K_RIGHT] and player.x < 1200 - player.width:
player.x += vel
if keys[pygame.K_UP] and player.y > 0:
player.y -= vel
if keys[pygame.K_DOWN] and player.y < 600 - player.height:
player.y += vel
# Game logic for walls and moving objects
for wall in walls:
# Check if the player rect collides with a wall rect.
if player.colliderect(wall):
print("Game over")
message_display("Game Over")
for movingrect in movingrects:
movingrect.update() # Movement and bounds checking.
if player.colliderect(movingrect):
print("Game over")
message_display("Game Over")
for rotatingrect in rotatingrects:
if player.colliderect(rotatingrect):
print("Game over")
# Drawing everything
win.fill(WHITE)
pygame.draw.rect(win, RED, player)
# Drawing walls and moving objects
for rotatingrect in rotatingrects:
pygame.draw.rect(win, BLACK, rotatingrect)
for wall in walls:
pygame.draw.rect(win, BLACK, wall)
for movingrect in movingrects:
pygame.draw.rect(win, GREEN, movingrect)
pygame.display.update()
clock.tick(60)
pygame.quit()
I think the problem is here:
if x + w > mouse[0] > x and y + h > mouse[1] > y:
pygame.draw.rect(win, ac, (x, y, w, h))
if click[0] == 1 and action != None:
if action == "Quit":
pygame.quit()
else:
pygame.draw.rect(win, ic, (x, y, w, h))
I just want to do if action == "Start":
play the game
if action == "Quit":
quit the game
Define two callback functions which you need to pass as the action argument to the button function.
In the quit_game function, you can call pygame.quit and sys.exit to close the window. The start_game function needs to be defined in the game_intro function, because we have to access the intro variable, with the help of the nonlocal keyword, and set it to False, so that the game_intro function will be terminated and the main loop can start.
def button(msg, x, y, w, h, ic, ac, action=None):
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(win, ac, (x, y, w, h))
if click[0] == 1 and action is not None:
action() # Call the callback function.
# etc. ...
def quit_game():
pygame.quit()
sys.exit() # `import sys` at the top of the file.
def game_intro():
intro = True
def start_game():
# Set the intro variable in the enclosing scope to False.
nonlocal intro
intro = False
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
win.fill(WHITE)
largeText2 = pygame.font.Font('freesansbold.ttf', 115)
TextSurf, TextRect = text_objects("Red Square", largeText2)
TextRect.center = ((600), (100))
win.blit(TextSurf, TextRect)
# Pass the two callback functions to `button`.
button("Start", 525, 250, 150, 60, YELLOW, BRIGHT_YELLOW, start_game)
button("Quit", 525, 350, 150, 60, YELLOW, BRIGHT_YELLOW, quit_game)
pygame.display.update()
clock.tick(15)
Also, don't call game_intro inside the main while loop.
game_intro()
run = True
while run:

Categories