I'm making a game and the game has a shop which you can buy tokens and if you click the button to buy them the mouse goes crazy..
This is a function which waits for the mouse click.
def button(text, x, y, width, height, inactive_color, active_color, action):
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:
if action == "buy_slowdown":
slowdown_powerup += 1
else:
pygame.draw.rect(gameDisplay, inactive_color, (x, y, width, height))
text_to_button(text, black, x, y, width, height)
Here is where the function is called:
def shop():
shop = True
while shop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
# BUY SLOWDOWN TOKENS
buyslowdownlabel = shopfont.render("Slowdown Tokens", 1, blue)
slowdown_string_label = smallfont.render(str(slowdown_powerup), 1, black)
gameDisplay.blit(buyslowdownlabel, [340, 190])
pygame.draw.rect(gameDisplay, grey, [380, 235, 75, 75])
gameDisplay.blit(slowdown_string_label, [410.5, 255.5])
button("Buy", 380, 320, 100, 70, dark_yellow, yellow, "buy_slowdown")
pygame.display.update()
Use event.MOUSEBUTTONDOWN instead of pygame.mouse.get_pressed() because event.MOUSEBUTTONDOWN is created only once when button changes state from not-pressed into pressed. pygame.mouse.get_pressed() is True all the time when you keep pressed - and because computer is faster than you then it can check pygame.mouse.get_pressed() thousands time when you click button.
#!/usr/bin/env python3
import pygame
# --- constants --- (UPPER_CASE names)
WHITE = (255,255,255)
BLACK = ( 0, 0, 0)
DARK_YELLOW = (200,200, 0)
YELLOW = (255,255, 0)
# --- functions ---
def button_create(text, rect, inactive_color, active_color, action):
font = pygame.font.Font(None, 40)
button_rect = pygame.Rect(rect)
text_buy = font.render(text, True, BLACK)
text_buy_rect = text_buy.get_rect(center=button_rect.center)
return [text_buy, text_buy_rect, button_rect, inactive_color, active_color, action, False]
def button_check(info, event):
text, text_rect, rect, inactive_color, active_color, action, hover = info
if event.type == pygame.MOUSEMOTION:
# hover = True/False
info[-1] = rect.collidepoint(event.pos)
elif event.type == pygame.MOUSEBUTTONDOWN:
if hover and action:
action()
def button_draw(screen, info):
text, text_rect, rect, inactive_color, active_color, action, hover = info
if hover:
color = active_color
else:
color = inactive_color
pygame.draw.rect(screen, color, rect)
screen.blit(text, text_rect)
# ---
def buy_slowdown(number=1):
global slowdown_powerup
slowdown_powerup += number
print('slowdown_powerup:', slowdown_powerup)
def buy_1():
buy_slowdown(1)
def buy_10():
buy_slowdown(10)
def buy_100():
buy_slowdown(100)
# --- main ---
# - init -
pygame.init()
screen = pygame.display.set_mode((800,600))
screen_rect = screen.get_rect()
# - objects -
slowdown_powerup = 0
button_1 = button_create("+1", (380, 235, 75, 75), DARK_YELLOW, YELLOW, buy_1)
button_2 = button_create("+10", (480, 235, 75, 75), DARK_YELLOW, YELLOW, buy_10)
button_3 = button_create("+100", (580, 235, 75, 75), DARK_YELLOW, YELLOW, buy_100)
# - mainloop -
shop = True
while shop:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
shop = False
button_check(button_1, event)
button_check(button_2, event)
button_check(button_3, event)
# --- draws ---
screen.fill(WHITE)
button_draw(screen, button_1)
button_draw(screen, button_2)
button_draw(screen, button_3)
pygame.display.update()
# - end -
pygame.quit()
Related
I have a button that changes color when the cursor hovers over it, and I want to make it so that when the button is pressed, it leaves the color that it has when the cursor hovers over it. There are my unsuccessful attempts to do this in the code. I think I made a mistake somewhere in the "draw" function in the "Button" class.
import pygame
import pygame_widgets as pw
pygame.init() # Инициализируем модуль pygame
width = 1134 # ширина игрового окна
height = 693 # высота игрового окна
fps = 30 # частота кадров в секунду
game_name = "Тренажёр" # название нашей игры
snd_dir = 'sound/' # Путь до папки со звуками
img_dir = 'images/' # Путь до папки со спрайтами
# Цвета
BLACK = "#000000"
WHITE = "#FFFFFF"
RED = "#FF0000"
GREEN = "#008000"
BLUE = "#0000FF"
CYAN = "#00FFFF"
OneNumber = list(range(1, 10))
TwoNumber = list(range(10, 100))
ThreeNumber = list(range(100, 1000))
button_sound = pygame.mixer.Sound(snd_dir + 'button.mp3')
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def print_text(message, x, y, font_color=(0, 0, 0), font_type='ttf/Ping Pong.ttf', font_size=30):
font_type = pygame.font.Font(font_type, font_size)
text = font_type.render(message, True, font_color)
screen.blit(text, (x, y))
class Button:
def __init__(self, width, height):
self.width = width
self.height = height
self.inactive_color = (255, 165, 0)
self.active_color = (13, 162, 58)
self.button_active = False
def draw(self,x ,y, message, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x < mouse[0] < x + self.width:
if y < mouse[1] < y + self.height:
pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
if click[0] == 1:
pygame.mixer.Sound.play(button_sound)
pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
self.button_active = True
pygame.time.delay(300)
print(self.button_active)
#if action is not None:
#action()
elif y < mouse[1] < y + self.height and self.button_active == True:
pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
elif x > mouse[0] > x + self.width and self.button_active == False:
pygame.draw.rect(screen, self.inactive_color, (x,y, self.width, self.height))
print_text(message, x + 10, y + 10)
BackGround = Background(img_dir + "fon.jpg", [0,0])
# Создаем игровой экран
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(game_name) # Заголовок окна
icon = pygame.image.load(img_dir + 'icon.png') # загружаем файл с иконкой
pygame.display.set_icon(icon) # устанавливаем иконку в окно
timer = pygame.time.Clock() # Создаем таймер pygame
run = True
button = Button(90, 50)
while run: # Начинаем бесконечный цикл
timer.tick(fps) # Контроль времени (обновление игры)
for event in pygame.event.get(): # Обработка ввода (события)
if event.type == pygame.QUIT: # Проверить закрытие окна
run = False # Завершаем игровой цикл
# Рендеринг (прорисовка)
screen.fill(WHITE) # Заливка заднего фона
screen.blit(BackGround.image, BackGround.rect)
button.draw(20, 100, "wow")
pygame.display.update() # Переворачиваем экран
pygame.quit() # Корректно завершаем игру
Test if the mouse is on the button:
button_rect = pygame.Rect(x, y, self.width, self.height)
mouse_on_button = button_rect.collidepoint(mouse)
Set self.button_active when the mouse is on the button and the mouse button is pressed:
if mouse_on_button and click[0] == 1:
self.button_active = True
Set the color depending on mouse_on_button and self.button_active:
color = self.inactive_color
if mouse_on_button or self.button_active:
color = self.active_color
pygame.draw.rect(screen, color, button_rect)
class Button:
class Button:
def __init__(self, width, height):
self.width = width
self.height = height
self.inactive_color = (255, 165, 0)
self.active_color = (13, 162, 58)
self.button_active = False
def draw(self,x ,y, message, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
button_rect = pygame.Rect(x, y, self.width, self.height)
mouse_on_button = button_rect.collidepoint(mouse)
if mouse_on_button and click[0] == 1:
self.button_active = True
color = self.inactive_color
if mouse_on_button or self.button_active:
color = self.active_color
pygame.draw.rect(screen, color, button_rect)
print_text(message, x + 10, y + 10)
However pygame.mouse.get_pressed() is not an event. If you want to toggle the button on and off you have to use the MOUSEBUTTONDOWN event. pygame.mouse.get_pressed() returns a list of Boolean values that represent the state (True or False) of all mouse buttons. The state of a button is True as long as a button is held down. The MOUSEBUTTONDOWN event occurs once when you click the mouse button and the MOUSEBUTTONUP event occurs once when the mouse button is released.
class Button:
def __init__(self, width, height):
self.width = width
self.height = height
self.inactive_color = (255, 165, 0)
self.active_color = (13, 162, 58)
self.button_active = False
def draw(self,x ,y, message, clicked, action=None):
mouse = pygame.mouse.get_pos()
button_rect = pygame.Rect(x, y, self.width, self.height)
mouse_on_button = button_rect.collidepoint(mouse)
if mouse_on_button and clicked:
self.button_active = not self.button_active
color = self.inactive_color
if mouse_on_button or self.button_active:
color = self.active_color
pygame.draw.rect(screen, color, button_rect)
print_text(message, x + 10, y + 10)
while run:
timer.tick(fps)
left_click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
left_click = event.button = 1
screen.fill(WHITE)
screen.blit(BackGround, (0, 0))
button.draw(20, 100, "wow", left_click)
pygame.display.update()
Also see Pygame mouse clicking detection.
I have a variable clickCounter that increases when the mouse button is clicked (when clicked on a button) and held. How do I stop the value from increasing when the button is held and only when it is clicked?
Button function (would it be easier to convert it to a class?):
def button(message, x, y, w, h, activeRGB, inactiveRGB, action=None): #example of color param for line 61/63
mouse = pygame.mouse.get_pos() #get location of mouse recorded by pygame
click = pygame.mouse.get_pressed()
global clickCounter
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, activeRGB, (x, y, w, h))
if click[0] ==1 and action != None:
if action == "countUP":
clickCounter+= 1
print(str(clickCounter))
pygame.display.update()
else:
pygame.draw.rect(gameDisplay, inactiveRGB, (x, y, w, h))
smallText = pygame.font.Font("freesansbold.ttf", 20) # constant has been declared, try deleting line when done w/ proj
textSurf, textRect = textBox(message, smallText)
textRect.center = ( (x + (w/2)), y+(h/2))
gameDisplay.blit(textSurf, textRect)
main loop
closeGame = False
while not closeGame: # this keeps the window from shutting down
for thing in pygame.event.get():
if thing.type == pygame.QUIT:
closeGame = True
print(thing)
gameDisplay.fill(white)
button("Click me!", 300, 300, 100, 100, blue, brightBlue, "countUP")
textObject(str(clickCounter), black, mediumText, 200, 200)
pygame.display.update()
clock.tick(20)
All code:
import pygame
pygame.init()
displayWidth = 700
displayHeight = displayWidth
gameDisplay = pygame.display.set_mode((700,700))
clock = pygame.time.Clock()
black = (0, 0, 0)
brightBlue = (0, 0, 225)
blue = (0, 0, 255)
white = (255, 255, 255) #predef colors to make temp rgb value coding easier
closeGame = False
mediumText = pygame.font.Font("freesansbold.ttf", 70) #initalize font
clickCounter = 0
#def fontSize (pxsize):
#pygame.font.Font("freesandsbold.ttf", pxsize)
def textObject (text, color, font, x, y):
storedGenerate = font.render(text, 1, ((color)))
gameDisplay.blit(storedGenerate, (x,y))
def textBox(text, font): #purely for redturning rectangle around font// used for btn function only, tObject for displaying text
textSurface = font.render(text, True, black) #swap black for rgb later
return textSurface, textSurface.get_rect()
def button(message, x, y, w, h, activeRGB, inactiveRGB, action=None): #example of color param for line 61/63
mouse = pygame.mouse.get_pos() #get location of mouse recorded by pygame
click = pygame.mouse.get_pressed()
global clickCounter
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, activeRGB, (x, y, w, h))
if click[0] ==1 and action != None:
if action == "countUP":
clickCounter+= 1
print(str(clickCounter))
pygame.display.update()
else:
pygame.draw.rect(gameDisplay, inactiveRGB, (x, y, w, h))
smallText = pygame.font.Font("freesansbold.ttf", 20) # constant has been declared, try deleting line when done w/ proj
textSurf, textRect = textBox(message, smallText)
textRect.center = ( (x + (w/2)), y+(h/2))
gameDisplay.blit(textSurf, textRect)
closeGame = False
while not closeGame: # this keeps the window from shutting down
for thing in pygame.event.get():
if thing.type == pygame.QUIT:
closeGame = True
print(thing)
gameDisplay.fill(white)
button("Click me!", 300, 300, 100, 100, blue, brightBlue, "countUP")
textObject(str(clickCounter), black, mediumText, 200, 200)
pygame.display.update()
clock.tick(20)
gameDisplay.fill(white)
pygame.display.update()
#logicloop()
pygame.quit()
quit()
My inital thought was to not use pygame.mouse.get_pressed() and instead use mousebutton down but was unable to get it to function properly. Any help is appreciated.
Evaluate the MOUSEBUTTONDOWN event instead of pygame.mouse.get_pressed. The event occurs a single time, when the mouse button is pressed.
Store the mouse button which was pressed to a variable mousebutton:
while not closeGame: # this keeps the window from shutting down
mousebutton = 0
for thing in pygame.event.get():
if thing.type == pygame.QUIT:
closeGame = True
elif thing.type == pygame.MOUSEBUTTONDOWN:
mousebutton = thing.button
Pass the variable to the function button:
button(mousebutton, "Click me!", 300, 300, 100, 100, blue, brightBlue, "countUP")
Evaluate if the left button was pressed in the fucntion:
def button(mousebutton, message, x, y, w, h, activeRGB, inactiveRGB, action=None):
mouse = pygame.mouse.get_pos() #get location of mouse recorded by pygame
global clickCounter
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, activeRGB, (x, y, w, h))
if mousebutton == 1 and action != None:
if action == "countUP":
clickCounter+= 1
print(str(clickCounter))
pygame.display.update()
else:
pygame.draw.rect(gameDisplay, inactiveRGB, (x, y, w, h))
# [...]
The .button attribute of the MOUSEBUTTONDOWN event returns 1 for the left button, 2 for the middle button, 3 fro the right button respectively 4 when the mouse wheel is rolled up, and 5 when the mouse wheel is rolled down.
I have some code that creates a button and code creating some text that is displayed on-screen. The goal is to have the button when clicked add 1 to the variable and have the changed reflected on screen (ie button is clicked the value on screen goes from 0 to 1 and then 1 to 2 and so on).
The button function:
def button(message, x, y, w, h, activeRGB, inactiveRGB, action=None): #example of color param for line 61/63
mouse = pygame.mouse.get_pos() #get location of mouse recorded by pygame
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, activeRGB, (x, y, w, h))
if click[0] ==1 and action != None:
if action == "countUP":
clickCounter+= 1
print(str(clickCounter)) # print to check that the button actually changes the value
pygame.display.update()
else:
pygame.draw.rect(gameDisplay, inactiveRGB, (x, y, w, h))
smallText = pygame.font.Font("freesansbold.ttf", 20)
textSurf, textRect = textBox(message, mediumText)
textRect.center = ( (x + (w/2)), y+(h/2))
gameDisplay.blit(textSurf, textRect)
The logic I have so far
gameDisplay.fill(white)
pygame.display.update()
clickCounter = str(clickCounter)
textObject(clickCounter, black, mediumText, 200, 200)
closeGame = False
while not closeGame: # this keeps the window from shutting down
for thing in pygame.event.get():
if thing.type == pygame.QUIT:
closeGame = True
print(thing)
button("Click me!", 300, 300, 100, 100, blue, brightBlue, "countUP")
pygame.display.update()
clock.tick(20)
gameDisplay.fill(white)
pygame.display.update()
#logicloop()
The button function does appear to be changing the value of clickCounter but it is not reflected on screen. My guess was that there is a missing blit command or screen update, though nothing i've tried has worked.
For whatever reason when I had this block of code set up as a function that is then called, it ran without the gameDisplay.fill(white) or any of the other on screen elements appearing, how would I go about setting it up as a function to make adding more to the logic easier?
All of the code:
import pygame
pygame.init()
displayWidth = 700
displayHeight = displayWidth
gameDisplay = pygame.display.set_mode((700,700))
clock = pygame.time.Clock()
black = (0, 0, 0)
brightBlue = (0, 0, 225)
blue = (0, 0, 255)
white = (255, 255, 255) #predef colors to make temp rgb value coding easier
closeGame = False
mediumText = pygame.font.Font("freesansbold.ttf", 70) #initalize font
clickCounter = 0
#def fontSize (pxsize):
#pygame.font.Font("freesandsbold.ttf", pxsize)
def textObject (text, color, font, x, y):
storedGenerate = font.render(text, 1, ((color)))
gameDisplay.blit(storedGenerate, (x,y))
def textBox(text, font): #purely for returning rectangle around font// used for btn function only, tObject for displaying text
textSurface = font.render(text, True, black) #swap black for rgb later
return textSurface, textSurface.get_rect()
def button(message, x, y, w, h, activeRGB, inactiveRGB, action=None): #example of color param for line 61/63
mouse = pygame.mouse.get_pos() #get location of mouse recorded by pygame
click = pygame.mouse.get_pressed()
global clickCounter
clickCounter = 0
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, activeRGB, (x, y, w, h))
if click[0] ==1 and action != None:
if action == "countUP":
clickCounter+= 1
print(str(clickCounter))
pygame.display.update()
else:
pygame.draw.rect(gameDisplay, inactiveRGB, (x, y, w, h))
#smallText = pygame.font.Font("freesansbold.ttf", 20) # constant has been declared, try deleting line when done w/ proj
smallText = pygame.font.SysFont('Times New Roman', 20)
textSurf, textRect = textBox(message, smallText)
textRect.center = ( (x + (w/2)), y+(h/2))
gameDisplay.blit(textSurf, textRect)
gameDisplay.fill(white)
pygame.display.update()
clickCounter = str(clickCounter)
textObject(clickCounter, black, mediumText, 200, 200)
closeGame = False
while not closeGame: # this keeps the window from shutting down
for thing in pygame.event.get():
if thing.type == pygame.QUIT:
closeGame = True
print(thing)
button("Click me!", 300, 300, 100, 100, blue, brightBlue, "countUP")
pygame.display.update()
clock.tick(20)
gameDisplay.fill(white)
pygame.display.update()
#logicloop()
pygame.quit()
quit()
The first issue is, that the number value stored in clickCounter is converted to a string.
Delete:
lickCounter = str(clickCounter)
In the function button, the value of clickCounter is continuously set 0. Delete this, too:
def button(message, x, y, w, h, activeRGB, inactiveRGB, action=None):
mouse = pygame.mouse.get_pos() #get location of mouse recorded by pygame
click = pygame.mouse.get_pressed()
global clickCounter
# clickCounter = 0 <------ delete
# [...]
You missed to draw the changed text in the main loop. Before the text is drawn the "old" text has to be cleared. Continuously redraw the entire scene in the main loop of the application.
This means the display has to be filled by the background color and the text and the button have to be redrawn:
while not closeGame: # this keeps the window from shutting down
for thing in pygame.event.get():
if thing.type == pygame.QUIT:
closeGame = True
print(thing)
gameDisplay.fill(white)
button("Click me!", 300, 300, 100, 100, blue, brightBlue, "countUP")
textObject(str(clickCounter), black, mediumText, 200, 200)
pygame.display.update()
clock.tick(20)
I'm trying to make a pause function I've tested this pause function separately and it seems to be working fine although there is nothing that it can do at the moment but it still shows. However, when I implement it in my game it doesn't work?
I've put it in different places before the while loop and in the while loop. When it's before the while loop the pause function will show first then mess up the game. When it's put in the while loop, it wont show at all even when 'p' is pressed.
What is the issue?
import pygame
import os
import sys
pygame.init()
pygame.font.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0, 0)
clock = pygame.time.Clock()
FPS = 60
DS = pygame.display.set_mode((900, 600))
sprite_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(sprite_image, (90, 100, 200), [25, 25], 25)
W, H = DS.get_size()
pause = True
st = pygame.font.SysFont("comicsans ms", 20)
lf = pygame.font.SysFont("comicsans ms", 120)
def paused():
DS.fill((50, 50, 70)) # Fill the display surface.
text_surf, text_rect = text_objects('Paused', (W/2, H/2), lf)
DS.blit(text_surf, text_rect)
# The buttons are dictionaries now.
resume_button = button('Resume', 150, 450, 100, 50, WHITE, WHITE)
quit_button = button('Quit', 350, 450, 100, 50, WHITE, WHITE)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # Should be pygame.quit not QUIT.
sys.exit() # Better use sys.exit() instead of quit().
elif event.type == pygame.MOUSEMOTION:
# Switch the active surface of the buttons
# if we're hovering over them.
for btn in (resume_button, quit_button):
if btn['rect'].collidepoint(event.pos):
btn['active_surface'] = btn['surface_hover']
else:
btn['active_surface'] = btn['surface']
elif event.type == pygame.MOUSEBUTTONDOWN:
# Check if the buttons were clicked.
if resume_button['rect'].collidepoint(event.pos):
return # Return to the main loop.
elif quit_button['rect'].collidepoint(event.pos):
pygame.quit()
sys.exit()
def text_objects(text, pos, font):
text_surface = font.render(text, True, BLACK)
return text_surface, text_surface.get_rect(center=pos)
def display_message(text):
lf = pygame.font.SysFont("comicsans", 120)
TextSurf, TextRect = text_objects(text, lf)
TextRect.center = ((W / 2)), ((H / 2))
DS.blit(TextSurf, TextRect)
pygame.display.update()
def button(msg, x, y, w, h, inactive_color, active_color):
"""Create a button dictionary with the images and a rect."""
text_surf, text_rect = text_objects(msg, (w/2, h/2), st)
# Create the normal and hover surfaces and blit the text onto them.
surface = pygame.Surface((w, h))
surface.fill(inactive_color)
surface.blit(text_surf, text_rect)
surface_hover = pygame.Surface((w, h))
surface_hover.fill(active_color)
surface_hover.blit(text_surf, text_rect)
return {'active_surface': surface,
'surface': surface,
'surface_hover': surface_hover,
'rect': pygame.Rect(x, y, w, h),
}
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
super(Platform, self).__init__()
self.image = pygame.Surface((w, h))
self.image.fill((90, 90, 120))
self.rect = self.image.get_rect(topleft=(x, y))
class Sprite(pygame.sprite.Sprite):
def __init__(self, pos):
super(Sprite, self).__init__()
self.image = sprite_image
self.rect = self.image.get_rect()
self.rect.center = pos
self._vx = 0
self._vy = 0
self._spritex = pos[0]
self._spritey = pos[1]
self.level = None
self._gravity = .9
def update(self):
# Adjust the x-position.
self._spritex += self._vx
self.rect.centerx = self._spritex # And update the rect.
block_hit_list = pygame.sprite.spritecollide(self, self.level, False)
for block in block_hit_list:
if self._vx > 0:
self.rect.right = block.rect.left
elif self._vx < 0:
# Otherwise if we are moving left, do the opposite.
self.rect.left = block.rect.right
self._spritex = self.rect.centerx # Update the position.
# Adjust the y-position.
self._vy += self._gravity # Accelerate downwards.
self._spritey += self._vy
self.rect.centery = self._spritey # And update the rect.
# Check and see if we hit anything
if block_hit_list == pygame.sprite.spritecollide(self, self.level, False):
for block in block_hit_list:
# Reset our position based on the top/bottom of the object.
if self._vy > 0:
self.rect.bottom = block.rect.top
elif self._vy < 0:
self.rect.top = block.rect.bottom
self._spritey = self.rect.centery # Update the position.
# Stop our vertical movement
self._vy = 0
def jump(self):
self.rect.y += 3
platform_hit_list = pygame.sprite.spritecollide(self, self.level, False)
self.rect.y -= 3
# If it is ok to jump, set our speed upwards
if len(platform_hit_list) > 0 or self.rect.bottom >= H:
self._vy = -17
def go_left(self):
# Called when the user hits the left arrow.
self._vx = -3
def go_right(self):
#Called when the user hits the right arrow.
self._vx = 3
def stop(self):
# Called when the user lets off the keyboard.
self._vx = 0
sprite = Sprite([60, 60])
active_sprite_list = pygame.sprite.Group(sprite)
sprite.level = pygame.sprite.Group()
# A list with rect style tuples (x, y, width, height).
rects = [
(20, 20, 150, 20), (120, 200, 200, 20), (400, 440, 300, 20),
(40, 100, 200, 20), (320, 300, 150, 20), (520, 550, 300, 20),
(600, 100, 150, 20), (300, 200, 200, 20), (290, 440, 300, 20),
(40, 100, 200, 20), (320, 300, 150, 20), (95, 550, 300, 20),
(250,300,150,20), (350,100,150,20), (450,50,270,20), (700,185,70,20),
(650,350,350,20)
]
# You can unpack the rect tuples directly in the head of the for loop.
for x, y, width, height in rects:
# Create a new platform instance and pass the coords, width and height.
platform = Platform(x, y, width, height)
# Add the new platform instance to the sprite groups.
sprite.level.add(platform)
active_sprite_list.add(platform)
active_sprite_list.add(sprite.level)
done = False
while not done:
def paused():
DS.fill((50, 50, 70)) # Fill the display surface.
text_surf, text_rect = text_objects('Paused', (W / 2, H / 2), lf)
DS.blit(text_surf, text_rect)
# The buttons are dictionaries now.
resume_button = button('Resume', 150, 450, 100, 50, WHITE, WHITE)
quit_button = button('Quit', 350, 450, 100, 50, WHITE, WHITE)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # Should be pygame.quit not QUIT.
sys.exit() # Better use sys.exit() instead of quit().
elif event.type == pygame.MOUSEMOTION:
# Switch the active surface of the buttons
# if we're hovering over them.
for btn in (resume_button, quit_button):
if btn['rect'].collidepoint(event.pos):
btn['active_surface'] = btn['surface_hover']
else:
btn['active_surface'] = btn['surface']
elif event.type == pygame.MOUSEBUTTONDOWN:
# Check if the buttons were clicked.
if resume_button['rect'].collidepoint(event.pos):
return # Return to the main loop.
elif quit_button['rect'].collidepoint(event.pos):
pygame.quit()
sys.exit()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
pause = True
paused()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
sprite.go_left()
pause = False
if event.key == pygame.K_RIGHT:
sprite.go_right()
pause = False
if event.key == pygame.K_UP:
sprite.jump()
pause = False
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and sprite._vx < 0:
sprite.stop()
pause = False
if event.key == pygame.K_RIGHT and sprite._vx > 0:
sprite.stop()
# If the player gets near the right side, shift the world left (-x)
if sprite.rect.right > W:
sprite.rect.right = W
# If the player gets near the left side, shift the world right (+x)
if sprite.rect.left < 0:
sprite.rect.left = 0
active_sprite_list.update()
DS.fill(WHITE)
# Blit the sprite's image at the sprite's rect.topleft position.
active_sprite_list.draw(DS)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
Take a look at this example. I use dictionaries for the buttons which consist of the images, the currently active image and a rect which is used for the collision detection and as the position. If the user clicks the resume button, I just return to the main game loop.
import sys
import pygame
pygame.init()
WHITE = (255, 255, 255)
GRAY = pygame.Color("gray30")
BLACK = (0, 0, 0, 0)
clock = pygame.time.Clock()
FPS = 60
DS = pygame.display.set_mode((900, 600))
W, H = DS.get_size()
# Define the fonts once when the program starts.
st = pygame.font.SysFont("comicsans ms", 20)
lf = pygame.font.SysFont("comicsans ms", 120)
def paused():
DS.fill((50, 50, 70)) # Fill the display surface.
text_surf, text_rect = text_objects('Paused', (W/2, H/2), lf)
DS.blit(text_surf, text_rect)
# The buttons are dictionaries now.
resume_button = button('Resume', 150, 450, 100, 50, WHITE, GRAY)
quit_button = button('Quit', 350, 450, 100, 50, WHITE, GRAY)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # Should be pygame.quit not QUIT.
sys.exit() # Better use sys.exit() instead of quit().
elif event.type == pygame.MOUSEMOTION:
# Switch the active surface of the buttons
# if we're hovering over them.
for btn in (resume_button, quit_button):
if btn['rect'].collidepoint(event.pos):
btn['active_surface'] = btn['surface_hover']
else:
btn['active_surface'] = btn['surface']
elif event.type == pygame.MOUSEBUTTONDOWN:
# Check if the buttons were clicked.
if resume_button['rect'].collidepoint(event.pos):
return # Return to the main loop.
elif quit_button['rect'].collidepoint(event.pos):
pygame.quit()
sys.exit()
# Draw the active surfaces at the rects of the buttons.
DS.blit(resume_button['active_surface'], resume_button['rect'])
DS.blit(quit_button['active_surface'], quit_button['rect'])
pygame.display.update()
clock.tick(15)
def text_objects(text, pos, font):
text_surface = font.render(text, True, BLACK)
return text_surface, text_surface.get_rect(center=pos)
def button(msg, x, y, w, h, inactive_color, active_color):
"""Create a button dictionary with the images and a rect."""
text_surf, text_rect = text_objects(msg, (w/2, h/2), st)
# Create the normal and hover surfaces and blit the text onto them.
surface = pygame.Surface((w, h))
surface.fill(inactive_color)
surface.blit(text_surf, text_rect)
surface_hover = pygame.Surface((w, h))
surface_hover.fill(active_color)
surface_hover.blit(text_surf, text_rect)
return {'active_surface': surface,
'surface': surface,
'surface_hover': surface_hover,
'rect': pygame.Rect(x, y, w, h),
}
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
paused()
DS.fill((50, 50, 50))
pygame.draw.circle(DS, (0, 90, 190), (100, 100), 50)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
I am trying to make a hangman game in pygame, but I cannot get buttons to work after they have been created from an event (in my case after pressing enter), they appear on screen but do not work ( rollover or activate events), please help!
from pygame.locals import *
import pygame, sys, eztext
import time
pygame.init()
Black = ( 0, 0, 0)
Grey = (100, 100, 100)
DarkGrey = (70, 70, 70)
White = (255, 255, 255)
Blue = ( 0, 0, 255)
NavyBlue = ( 60, 60, 100)
Cyan = ( 0, 255, 255)
Purple = (255, 0, 255)
Red = (255, 0, 0)
DarkRed = (180, 0, 0)
Orange = (255, 128, 0)
Yellow = (255, 255, 0)
Green = ( 0, 255, 0)
DarkGreen = ( 0, 180, 0)
LightBlue = ( 90, 164, 213)
Magenta = (153, 0, 76)
screen = pygame.display.set_mode((1000,600),0,32)
screen.fill(Orange)
txtbx = eztext.Input(maxlength=45, color=(0,0,0), prompt='Enter username: ')
def text_objects(text, font):
textSurface = font.render(text, True, Black)
return textSurface, textSurface.get_rect()
smallText = pygame.font.Font("freesansbold.ttf",20)
def button(msg,x,y,w,h,ic,ac,action=None):
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 and action!= None:
if action == "Quit":
pygame.quit()
if action == "Single player":
pygame.draw.rect(screen, Green,(10, 10, 500, 500))
else:
pygame.draw.rect(screen, 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)))
screen.blit(textSurf, textRect)
while True:
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
string1 = txtbx.value
pygame.display.update()
time.sleep(0.02)
pygame.draw.rect(screen, Green,(10, 100, 500, 50))
clock = pygame.time.Clock()
clock.tick(30)
events = pygame.event.get()
txtbx.set_pos(10,110)
label = smallText.render("Enter you user name and then press enter!", 1, (0,0,0))
screen.blit(label, (10, 10))
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_KP_ENTER or event.key == pygame.K_RETURN:
button("Single player",10, 400, 150, 50, Green, DarkGreen,"Single player")
if string1 in open('Users.txt').read():
username = string1
Welcomeold1 = smallText.render('Welcome back', 1, (0,0,0))
screen.blit(Welcomeold1, (10, 200))
Welcomeold2 = smallText.render(username, 1, (0,0,0))
screen.blit(Welcomeold2, (160, 200))
else:
username = string1
f = open('Users.txt','a')
string1 = txtbx.value
f.write(string1)
f.write('\n')
f.close()
Welcomenew1 = smallText.render('Welcome to my game', 1, (0,0,0))
screen.blit(Welcomenew1, (10, 200))
Welcomenew2 = smallText.render(username, 1, (0,0,0))
screen.blit(Welcomenew2, (230, 200))
if event.key == pygame.K_BACKSPACE:
screen.fill(Orange)
txtbx.update(events)
txtbx.draw(screen)
pygame.display.flip()
if __name__ == '__main__': main()
button function is inside event.key so it will work only when RETURN is pressed, released, and pressed again, and released again, etc.
You should create class with method draw and handle_event to use this method in mainloop in different places. You can create button in event.key but you have to check button pressed in different place.
-
Simple button. Full working example.
When you press button function do_it is called and background changed color.
#
# pygame (simple) template - by furas
#
# ---------------------------------------------------------------------
import pygame
import pygame.gfxdraw
import random
# === CONSTANS === (UPPER_CASE names)
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
CYAN = ( 0, 255, 255)
MAGENTA = (255, 0, 255)
YELLOW = (255, 255, 0)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
# === CLASSES === (CamelCase names)
class Button():
def __init__(self, text, x, y, width, height, on_click=None, fg_color=RED, bg_color=WHITE, font_size=35):
self.text = text
font = pygame.font.SysFont(None, font_size)
# rectangle - without position, to put text in correct place
self.rect = pygame.Rect(0, 0, width, height)
# normal button - fill background
self.normal_image = pygame.surface.Surface( self.rect.size )
self.normal_image.fill(bg_color)
# normal button - add text
text_image = font.render(text, True, fg_color)
text_rect = text_image.get_rect(center=self.rect.center)
self.normal_image.blit(text_image, text_rect)
# hover button - fill background
self.hover_image = pygame.surface.Surface( self.rect.size )
self.hover_image.fill(fg_color)
# hover button - add text
text_image = font.render(text, True, bg_color)
text_rect = text_image.get_rect(center=self.rect.center)
self.hover_image.blit(text_image, text_rect)
# set position
self.rect.x = x
self.rect.y = y
# other
self.on_click = on_click
self.hover = False
def draw(self, surface):
# draw normal or hover image
if self.hover:
surface.blit(self.hover_image, self.rect)
else:
surface.blit(self.normal_image, self.rect)
def event_handler(self, event):
# is mouse over button ?
if event.type == pygame.MOUSEMOTION:
self.hover = self.rect.collidepoint(event.pos)
# is mouse clicked ? run function.
if event.type == pygame.MOUSEBUTTONDOWN:
if self.hover and self.on_click:
self.on_click()
# === FUNCTIONS === (lower_case names)
def do_it():
global background_color
if background_color == BLACK:
background_color = RED
elif background_color == RED:
background_color = GREEN
else:
background_color = BLACK
print("Button pressed")
# === MAIN ===
# --- init ---
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
# --- objects ---
# create button
button = Button('CHANGE COLOR', 0, 0, 300, 50, on_click=do_it)
# add function later
#button_quit.on_click = do_it
# center button
button.rect.center = screen_rect.center
# default bacground color - changed in do_it()
background_color = BLACK
# --- mainloop ---
clock = pygame.time.Clock()
is_running = True
while is_running:
# --- events ---
for event in pygame.event.get():
# --- global events ---
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
is_running = False
# --- objects events ---
button.event_handler(event)
# --- updates ---
# empty
# --- draws ---
# clear buffer
screen.fill(background_color)
# draw all objects - widgets (buttons, labels)
button.draw(screen)
clock.tick(25)
# send buffer to video card
pygame.display.update()
# --- the end ---
pygame.quit()