Related
This question already has answers here:
Pygame mouse clicking detection
(4 answers)
Closed 1 year ago.
This post was edited and submitted for review 1 year ago and failed to reopen the post:
Duplicate This question has been answered, is not unique, and doesn’t differentiate itself from another question.
So I'm working on a snake game and I ran into some problems with the button class. I would like to implement two buttons at the end of the game. One in order to restart the game, the other one to close the game. I tested the buttons and they should be working. I just don't know where to draw them, because when I draw them at the end, I cannot press the button, because the game freezes due to the game.sleep() command. The game closes on itself, that's why I added a delay. I drew the buttons al the way at the end.
I edited the code, so only the button code is shown below with the gameloop.
clicked = False
font = pygame.font.SysFont('Constantia', 30)
class button():
# colours for button and text
button_col = (255, 0, 0)
hover_col = (75, 225, 255)
click_col = (50, 150, 255)
text_col = BLACK
width = 180
height = 70
def __init__(self, x, y, text):
self.x = x
self.y = y
self.text = text
def draw_button(self):
global clicked
action = False
# get mouse position
pos = pygame.mouse.get_pos()
# create pygame Rect object for the button
button_rect = Rect(self.x, self.y, self.width, self.height)
# check mouseover and clicked conditions
if button_rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1:
clicked = True
pygame.draw.rect(SCREEN, self.click_col, button_rect)
elif pygame.mouse.get_pressed()[0] == 0 and clicked == True:
clicked = False
action = True
else:
pygame.draw.rect(SCREEN, self.hover_col, button_rect)
else:
pygame.draw.rect(SCREEN, self.button_col, button_rect)
# add shading to button
pygame.draw.line(SCREEN, WHITE, (self.x, self.y),
(self.x + self.width, self.y), 2)
pygame.draw.line(SCREEN, WHITE, (self.x, self.y),
(self.x, self.y + self.height), 2)
pygame.draw.line(SCREEN, BLACK, (self.x, self.y + self.height),
(self.x + self.width, self.y + self.height), 2)
pygame.draw.line(SCREEN, BLACK, (self.x + self.width, self.y),
(self.x + self.width, self.y + self.height), 2)
# add text to button
text_img = font.render(self.text, True, self.text_col)
text_len = text_img.get_width()
SCREEN.blit(text_img, (self.x + int(self.width / 2) -
int(text_len / 2), self.y + 25))
return action
again = button(75, 200, 'Play Again?')
quit = button(325, 200, 'Quit?')
def main():
RUN = True
SNAKE_POS_X = BLOCKSIZE
SNAKE_POS_Y = BLOCKSIZE
SNAKE_POS_X_CHANGE = 0
SNAKE_POS_Y_CHANGE = 0
LENGTH_OF_SNAKE = 1
global FOOD_POS_X, FOOD_POS_Y
FOOD_POS_X = round(random.randrange(
0, WIDTH - BLOCKSIZE) / BLOCKSIZE) * BLOCKSIZE
FOOD_POS_Y = round(random.randrange(
0, HEIGHT - BLOCKSIZE) / BLOCKSIZE) * BLOCKSIZE
while RUN:
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUN = False
# snake_movement
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
SNAKE_POS_X_CHANGE = 0
SNAKE_POS_Y_CHANGE = -BLOCKSIZE
elif event.key == pygame.K_DOWN:
SNAKE_POS_X_CHANGE = 0
SNAKE_POS_Y_CHANGE = BLOCKSIZE
elif event.key == pygame.K_RIGHT:
SNAKE_POS_X_CHANGE = BLOCKSIZE
SNAKE_POS_Y_CHANGE = 0
elif event.key == pygame.K_LEFT:
SNAKE_POS_X_CHANGE = -BLOCKSIZE
SNAKE_POS_Y_CHANGE = 0
if SNAKE_POS_X >= WIDTH or SNAKE_POS_X < 0 or SNAKE_POS_Y >= HEIGHT or SNAKE_POS_Y < 0:
RUN = False
SNAKE_POS_X += SNAKE_POS_X_CHANGE
SNAKE_POS_Y += SNAKE_POS_Y_CHANGE
SCREEN.fill(BISQUE2)
checkerboard()
food()
SNAKE_HEAD = []
SNAKE_HEAD.append(SNAKE_POS_X)
SNAKE_HEAD.append(SNAKE_POS_Y)
SNAKE_LIST.append(SNAKE_HEAD)
if len(SNAKE_LIST) > LENGTH_OF_SNAKE:
del SNAKE_LIST[0]
for x in SNAKE_LIST[:-1]:
if x == SNAKE_HEAD:
RUN = False
snake(BLOCKSIZE, SNAKE_LIST)
score(LENGTH_OF_SNAKE - 1)
# draw_grid()
CLOCK.tick(FPS)
pygame.display.update()
if SNAKE_POS_X == FOOD_POS_X and SNAKE_POS_Y == FOOD_POS_Y:
FOOD_POS_X = round(random.randrange(
0, WIDTH - BLOCKSIZE) / BLOCKSIZE) * BLOCKSIZE
FOOD_POS_Y = round(random.randrange(
0, HEIGHT - BLOCKSIZE) / BLOCKSIZE) * BLOCKSIZE
LENGTH_OF_SNAKE += 1
CRUNCH.play()
game_over_message("Game Over!", BLACK)
GAME_OVER_SOUND.play()
# pygame.display.update()
if again.draw_button():
main()
if quit.draw_button():
pygame.quit()
pygame.display.update()
time.sleep(2)
pygame.quit()
main()
Is it because you do not have FOOD_POS_X, FOOD_POS_Y defined as this I got errors from these when I tried to run your code
I am working on my first python project. I have some buttons, 'Yes' and 'No' after the initial question of "do you want to play?". When I press no it closes the game. When I click yes, I'd like it for it to jump to the next function where it displays the text "so you've decided to play", and although I can see it jump to the next text for a little bit, it quickly switches back to the previous question and buttons of "Do you wish to play?" with the Yes and No buttons. How can I fix this? I've tried to order pygame.display.update() in different ways but so far no luck. Any help would be appreciated, thanks guys. Here is my code:
import pygame
pygame.init()
win=pygame.display.set_mode((800,700))
win.fill((255,255,255))
our_game_display=pygame.Surface((800,700))
font_name = pygame.font.get_default_font()
class button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 60)
text = font.render(self.text, 1, (255, 255, 255))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def isOver(self, pos):
# Pos is the mouse position or a tuple of (x,y) coordinates
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
def yes_no_choice():
#win.fill((0,0,0))
YesButton.draw(win,(255,255,255))
NoButton.draw(win,(255,255,255))
def draw_text(text, size, x, y):
pygame.font.init()
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, (255,255,255))
text_rect = text_surface.get_rect()
text_rect.center = (x, y)
our_game_display.blit(text_surface, text_rect)
def beginning_question():
our_game_display.fill((0, 0, 0))
draw_text('The story of this game depends on your choices. Do you wish to play?', 20, 800 / 2, 700 / 2 - 100)
win.blit(our_game_display, (0, 0))
yes_no_choice()
pygame.display.update()
def begin_game():
our_game_display.fill((0,0,0))
draw_text("So you've decided to play...very well.", 20, 800 / 2, 700 / 2 - 100)
win.blit(our_game_display,(0,0))
pygame.display.update()
#game loop
running = True
YesButton=button((0,0,0),100,500,250,100,'Yes')
NoButton=button((0,0,0),450,500,250,100,'No')
while running:
beginning_question()
for event in pygame.event.get():
pos=pygame.mouse.get_pos()
if event.type==pygame.QUIT:
running = False
pygame.quit()
quit()
if event.type==pygame.MOUSEBUTTONDOWN:
if YesButton.isOver(pos):
begin_game()
if NoButton.isOver(pos):
running=False
pygame.quit()
quit()
if event.type==pygame.MOUSEMOTION:
if YesButton.isOver(pos):
YesButton.color=(0,0,139)
elif NoButton.isOver(pos):
NoButton.color=(0,0,139)
else:
YesButton.color=(0,0,0)
NoButton.color=(0,0,0)
The reason why it only flashes for a second and then switches back is because the drawing for the "So you've decided to play" only happens in frames where you click the yes button. It should happen continuously after you hit the yest button. Here is one potential solution:
Have a boolean variable game_begun to keep track of whether the game has begun or not (initially false):
YesButton = button((0, 0, 0), 100, 500, 250, 100, 'Yes')
NoButton = button((0, 0, 0), 450, 500, 250, 100, 'No')
game_begun = False # New variable
After the user clicks the yes button, set that variable to true:
if event.type == pygame.MOUSEBUTTONDOWN:
if YesButton.isOver(pos):
game_begun = True
if NoButton.isOver(pos):
running = False
pygame.quit()
quit()
Finally, in the game loop (not the event loop), have a condition to check if the game has begun, and if so, then call the begin_game function. If not, then draw the beginning question:
if game_begun:
begin_game()
else:
beginning_question()
This might not be the absolute best solution, but it should suffice for your first python project. As you add more states to your game, you should use a string variable to keep track of you game state instead of a boolean. Happy coding!
I'm making a simple text based RPG game in pygame. I'm using a button class (not mine, but fairly simple to use and integrate) to add buttons with choices in them. However, it seems a button on the second slide, the 'Sit and Wait' choice, is overlapping with the 'No' button on the first slide. Since the No button closes the game, pressing on Sit and Wait seems to close the game also. I've tried ordering the if-else statements in different ways, but every other button seems fine. Any tips? Thank you. Here is my code, sorry I know it's a lot but I'm a beginner and not great at condensing yet:
import pygame
pygame.init()
win=pygame.display.set_mode((800,700))
win.fill((255,255,255))
our_game_display=pygame.Surface((800,700))
font_name = pygame.font.get_default_font()
class button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 30)
text = font.render(self.text, 1, (255, 255, 255))
win.blit(text, (self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def isOver(self, pos):
# Pos is the mouse position or a tuple of (x,y) coordinates
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
def draw_text(text, size, x, y):
pygame.font.init()
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, (255,255,255))
text_rect = text_surface.get_rect()
text_rect.center = (x, y)
our_game_display.blit(text_surface, text_rect)
def yell_choice():
our_game_display.fill((0, 0, 0))
draw_text('You yell to see if anyone can hear you.', 20, 800 / 2, 700 / 2 - 100)
win.blit(our_game_display, (0, 0))
pygame.display.update()
def sit_choice():
our_game_display.fill((0,0,0))
draw_text('So you decided to sit and wait', 20, 800 / 2, 700 / 2 - 100)
win.blit(our_game_display,(0,0))
pygame.display.update()
def search_choice():
our_game_display.fill((0,0,0))
draw_text('So you decided to search', 20, 800 / 2, 700 / 2 - 100)
win.blit(our_game_display,(0,0))
pygame.display.update()
def beginning_question():
our_game_display.fill((0, 0, 0))
draw_text('The story of this game depends on your choices. Do you wish to play?', 20, 800 / 2, 700 / 2 - 100)
win.blit(our_game_display, (0, 0))
YesButton.draw(win, (255, 255, 255))
NoButton.draw(win, (255, 255, 255))
pygame.display.update()
def begin_game():
our_game_display.fill((0,0,0))
draw_text("You wake up, or...at least you think you do. Even though your eyes are open,", 20, 800 / 2, 700 / 2 - 300)
draw_text("they still can't detect anything in the complete darkness that surrounds you.", 20, 800 / 2, 700 / 2 - 270)
draw_text("What do you do?", 20, 800 / 2, 700 / 2 - 210)
win.blit(our_game_display,(0,0))
SitWaitButton.draw(win,(255,255,255))
SearchButton.draw(win,(255,255,255))
YellHelpButton.draw(win,(255,255,255))
pygame.display.update()
#game loop
running = True
#color,x,y,width,height
YesButton=button((0,0,0),100,500,250,100,'Yes')
NoButton=button((0,0,0),450,500,250,100,'No')
SitWaitButton=button((0,0,0),500,500,200,50,'Sit and wait..')
SearchButton=button((0,0,0),250,600,600,50,'Get up and try to search your surroundings.')
YellHelpButton=button((0,0,0),250,400,600,50,'Yell to see if anyone is there.')
game_begun='Input' #beginning choice variable
search_option='Input'#search variable
sit_option='Input' #sitting variable
yell_option = 'Input'
while running:
if search_option=='Go':
search_choice()
elif sit_option=='Go':
sit_choice()
elif yell_option=='Go':
yell_choice()
elif game_begun=='Go':
begin_game()
else:
beginning_question()
for event in pygame.event.get():
pos=pygame.mouse.get_pos()
if event.type==pygame.QUIT:
running = False
pygame.quit()
quit()
#yes and no buttons for beginning question
if event.type==pygame.MOUSEBUTTONDOWN:
if SitWaitButton.isOver(pos):
sit_option = 'Go'
print("this button is working")
if SearchButton.isOver(pos):
search_option = 'Go'
print("this button is working")
if YellHelpButton.isOver(pos):
yell_option='Go'
print("this button is working")
if YesButton.isOver(pos):
game_begun='Go'
if NoButton.isOver(pos):
running=False
pygame.quit()
quit()
if event.type==pygame.MOUSEMOTION:
if YellHelpButton.isOver(pos):
YellHelpButton.color = (0, 0, 139)
elif SitWaitButton.isOver(pos):
SitWaitButton.color = (0, 0, 139)
elif SearchButton.isOver(pos):
SearchButton.color = (0, 0, 139)
elif YesButton.isOver(pos):
YesButton.color=(0,0,139)
elif NoButton.isOver(pos):
NoButton.color=(0,0,139)
else:
YesButton.color=(0,0,0)
NoButton.color=(0,0,0)
YellHelpButton.color = (0, 0, 0)
SitWaitButton.color = (0, 0, 0)
SearchButton.color = (0, 0, 0)
You can use an if-elif instruction like below, so that if the user clicks on the mouse while cursor is over the sit and wait button then you don't check if it's also over the quit button (and thus you don't quit the game):
if SitWaitButton.isOver(pos):
sit_option = 'Go'
print("this button is working")
elif NoButton.isOver(pos):
pygame.quit()
quit()
A cleaner solution would however be to remember which buttons are actually currently displayed. For example, you could have a list of visible buttons and check if the cursor is over a button only if this one is in the list.
On a side note, your running variable is useless: your main loop is while running but as soon as running is set to False you just quit the game immediately. So the condition of your loop never evaluates to False.
I'm using Python 3.7.4 and Pygame 1.9.6. This is kind of my first time with an object oriented in Python; and same with creating buttons. I'm just testing this, because I want to create a button for my Space Invaders game. I'm just unsure if this is an easy way to do it or what. And when I want to create another button. Do I just do what I did with the 'play_again_button'.
I came up with:
import pygame
class button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, screen, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(screen, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('freesansbold.ttf', 60)
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 isOver(self, pos):
# Pos is the mouse position or a tuple of (x,y) coordinates
if self.x < pos[0] < self.x + self.width:
if self.y < pos[1] < self.y + self.height:
return True
return False
pygame.init()
screen = pygame.display.set_mode((800, 600))
running = True
play_again_button = button((20, 20, 20), (340, 340), 20, 30, 20, 'Play again')
while running:
pygame.display.update()
play_again_button.draw(screen)
screen.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if play_again_button.isOver(1):
print("Clicked button")
if event.type == pygame.MOUSEMOTION:
if play_again_button.isOver(0):
play_again_button.color = (255, 0, 0)
else:
play_again_button.color = (0, 255, 0)
Anyways let me know if I should do a button like this or not. I always appreciate the feed back.
Making a button as an object is a good idea because it makes the code easier to understand, as it wraps up all the button properties into a single data structure, with member functions to act on it.
There's a couple of bugs in your code but it's mostly OK.
When you create the button, you're passing too many parameters, and some are tuples rather than individual integers:
play_again_button = button((20, 20, 20), (340, 340), 20, 30, 20, 'Play again') # BROKEN
play_again_button = button((20, 20, 20), 340, 340, 20, 30, 'Play again') # FIXED
Where the code does the mouse-position check, it's passing 1 or 0, and not the mouse event.pos, this is an easy fix:
if play_again_button.isOver( 1 ):
if play_again_button.isOver( event.pos ): # FIXED
Similarly for the mouse click-check.
And Finally the screen-painting is being done in a reverse order, so that the buttons is drawn before the screen is erased, so you never see the button.
Below is code that works. I moved the offset border code inside the button class.
According to Python Style Guide PEP8, class-names Should Be Capitalised too.
Ref:
import pygame
class Button():
def __init__(self, color, x, y, width, height, text='', outline=(0,0,0)):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.outline = outline
def draw(self, screen):
# Call this method to draw the button on the screen
if self.outline:
pygame.draw.rect(screen, self.outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0 )
pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('freesansbold.ttf', 60)
text = font.render(self.text, 1, (0, 0, 0))
screen.blit(text, (
self.x + int(self.width / 2 - text.get_width() / 2), self.y + int(self.height / 2 - text.get_height() / 2)))
def setColor( self, new_colour ):
self.color = new_color
def isOver(self, pos):
# Pos is the mouse position or a tuple of (x,y) coordinates
if self.x < pos[0] < self.x + self.width:
if self.y < pos[1] < self.y + self.height:
return True
return False
pygame.init()
screen = pygame.display.set_mode((800, 600))
running = True
DARK_GREY= ( 20, 20, 20 )
play_again_button = Button( DARK_GREY, 340, 340, 20, 30, 'Play again')
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if play_again_button.isOver( event.pos ):
print("Clicked button")
if event.type == pygame.MOUSEMOTION:
if play_again_button.isOver( event.pos ):
play_again_button.setColor( (255, 0, 0) )
else:
play_again_button.setColor( (0, 255, 0) )
screen.fill((255, 255, 255))
play_again_button.draw(screen)
pygame.display.update()
I'm trying to learn OOP with pygame and make a simple game, I'm loosely following a tutorial, but have tried to modify it to fit my own needs and now it's not working. I'm trying to draw a white rectangle onto a black window, the tutorial draws a blue circle on a black window and when I replace the circle to a rectangle it doesn't work.
My code is sepereated into 2 different files heres the first file:
import pygame
import LanderHandler
black = (0, 0, 0)
white = (255, 255, 255)
green = (0, 255, 0)
red = (255, 0, 0)
class MainLoop(object):
def __init__(self, width=640, height=400):
pygame.init()
pygame.display.set_caption("Lander Game")
self.width = width
self.height = height
self.screen = pygame.display.set_mode((self.width, self.height), pygame.DOUBLEBUF)
self.background = pygame.Surface(self.screen.get_size()).convert()
def paint(self):
lander = LanderHandler.Lander()
lander.blit(self.background)
def run(self):
self.paint()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
# call with width of window and fps
MainLoop().run()
And my second file:
import pygame
black = (0, 0, 0)
white = (255, 255, 255)
green = (0, 255, 0)
red = (255, 0, 0)
class Lander(object):
def __init__(self, height=10, width=10, color=white, x=320, y=240):
self.x = x
self.y = y
self.height = height
self.width = width
self.surface = pygame.Surface((2 * self.height, 2 * self.width))
self.color = color
pygame.draw.rect(self.surface, white, (self.height, self.height, self.width, self.width))
def blit(self, background):
"""blit the Ball on the background"""
background.blit(self.surface, (self.x, self.y))
def move(self, change_x, change_y):
self.change_x = change_x
self.change_y = change_y
self.x += self.change_x
self.y += self.change_y
if self.x > 300 or self.x < 0:
self.change_x = -self.change_x
if self.y > 300 or self.y < 0:
self.change_y = -self.change_y
Any help or pointing me in the right direction would be amazing thank you.
P.S. I get no running errors and a black window does pop up, but without a white rectangle.
Problem is because you draw rectangle on surface self.background
lander.blit(self.background)
but you never blit self.background on self.screen which is main buffer and which is send on monitor when you do
pygame.display.flip()
So you can draw directly on self.screen
lander.blit(self.screen)
or you have to blit self.background on self.screen
lander.blit(self.background)
self.screen.blit(self.background, (0,0))
You shouldn't create a function with the name blit because it might get in the way of the actual blit function. Also right here in the second code:
pygame.draw.rect(self.surface, white, (self.height, self.height, self.width, self.width))
you should use surface