2

I am currently having trouble with buttons in pygame. At the moment I am testing myself in pygame and I am trying to create a flappy bird type of game. What I am trying to achieve is that when I click on the play button on the main menu it will change the button sprite and run the main game.

I have managed to define the button function and get it to switch to the main game when I click. The only problem is that it doesn't show a different sprite when clicked and I can click anywhere in the application to switch to the main game instead of having to click on the button.

Any help would be much appreciated.

Thanks in Advance

import time
import random

import pygame
from pygame.locals import *

pygame.init()

#Predefined Colors
white = (255,255,255)
black = (0,0,0)

red = (200,0,0)
light_red = (255,0,0)

yellow = (200,200,0)
light_yellow = (255,255,0)

green = (34,177,76)
light_green = (0,255,0)

blue = (0,0,255)
light_blue = (0, 0, 200)

player_list = (
    # Red Bird
    ('assets/sprites/redbird-upflap.png', 'assets/sprites/redbird-midflap.png',
     'assets/sprites/redbird-downflap.png'),

    # Blue Bird
    ('assets/sprites/bluebird-upflap.png', 'assets/sprites/bluebird-midflap.png',
     'assets/sprites/bluebird-downflap.png'),

    # Yellow Bird
    ('assets/sprites/yellowbird-upflap.png', 'assets/sprites/yellowbird-midflap.png',
     'assets/sprites/yellowbird-downflap.png')
)

background_list = (
    ('assets/sprites/background-day.png', 'assets/sprites/background-night.png')
)

pipe_list = (
    ('assets/sprites/pipe-green.png', 'assets/sprites/pipe-red.png')
)

FPS = 30

images, sounds = {}, {}

def main():
    global base_x, base_y, clock, gameDisplay, display_height, display_width
    display_width = 288
    display_height = 512

    base_x = 0
    base_y = display_height * 0.79

    clock = pygame.time.Clock()
    gameDisplay = pygame.display.set_mode((display_width, display_height))

    pygame.display.set_caption("Flappy Bird")

    #Loading icon sprite
    images['Icon'] = pygame.image.load('assets/sprites/yellowbird-midflap.png')
    pygame.display.set_icon(images['Icon'])

    #Loading all the Numbers sprites
    images['Numbers'] = (
        pygame.image.load('assets/sprites/0.png').convert_alpha(),
        pygame.image.load('assets/sprites/1.png').convert_alpha(),
        pygame.image.load('assets/sprites/2.png').convert_alpha(),
        pygame.image.load('assets/sprites/3.png').convert_alpha(),
        pygame.image.load('assets/sprites/4.png').convert_alpha(),
        pygame.image.load('assets/sprites/5.png').convert_alpha(),
        pygame.image.load('assets/sprites/6.png').convert_alpha(),
        pygame.image.load('assets/sprites/7.png').convert_alpha(),
        pygame.image.load('assets/sprites/8.png').convert_alpha(),
        pygame.image.load('assets/sprites/9.png').convert_alpha()
    )

    #Game Over Sprite
    images['Game Over'] = pygame.image.load('assets/sprites/gameover.png').convert_alpha()
    #Starting Game sprite
    images['Starting Game'] = pygame.image.load('assets/sprites/startgame-screen.png').convert_alpha()
    #Flappy Bird Logo sprite
    images['Flappy Bird Logo'] = pygame.image.load('assets/sprites/flappybird-logo.png').convert_alpha()
    #Base Ground sprite
    images['Base Ground'] = pygame.image.load('assets/sprites/base.png').convert_alpha()
    #Play Button Up sprite
    images['Play Button Up'] = pygame.image.load('assets/sprites/playbutton-up.png').convert_alpha()
    #Play Button Down sprite
    images['Play Button Down'] = pygame.image.load('assets/sprites/playbutton-down.png').convert_alpha()
    #Quit Button Up sprite
    #images['Quit Button Up'] = pygame.image.load('assets/sprites/quitbutton-up.png').convert_alpha()
    #Quit Button Down sprite
    #images['Quit Button Down'] = pygame.image.load('assets/sprites/quitbutton-down.png').convert_alpha()

    #Sounds
    # sounds['Die'] = pygame.mixer.Sound('assets/audio/die.wav')
    # sounds['Hit'] = pygame.mixer.Sound('assets/audio/hit.wav')
    # sounds['Point'] = pygame.mixer.Sound('assets/audio/point.wav')
    # sounds['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh.wav')
    # sounds['wing'] = pygame.mixer.Sound('assets/audio/wing.wav')

    while True:
        #Select random Background sprites
        random_background = random.randint(0, len(background_list) - 1)
        images['Background'] = pygame.image.load(background_list[random_background]).convert()

        #Select random Player sprites
        random_player = random.randint(0, len(player_list) - 1)
        images['Player'] = (
            pygame.image.load(player_list[random_player][0]).convert_alpha(),
            pygame.image.load(player_list[random_player][1]).convert_alpha(),
            pygame.image.load(player_list[random_player][2]).convert_alpha()
        )

        #Select random Pipe sprite
        random_pipe = random.randint(0, len(pipe_list) - 1)
        images['Pipe'] = pygame.image.load(pipe_list[random_pipe])

        main_menu()

    pygame.display.update()
    clock.tick(FPS)

def button(action = None):
    cur = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()

    if action == 'Play':
        button = images['Play Button Up'].get_rect()

        for event in pygame.event.get():
            if click[0] == 1:
                if button.collidepoint(cur):
                    print ('Mouse Over')
                    images['Play Button Down']
                    main_game()

    else:
        gameDisplay.blit(images['Play Button Up'], (0, -10))




def main_menu():
    global player_index, player_x, player_y
    player_index = 0

    player_x = int(display_width * 0.2)
    player_y = int((display_height - images['Player']       [0].get_height()) / 2)

    menu = True

    while menu:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        gameDisplay.blit(images['Background'], (0, 0))
        gameDisplay.blit(images['Base Ground'], (base_x, base_y))
        gameDisplay.blit(images['Flappy Bird Logo'], (50, -30))
        gameDisplay.blit(images['Player'][player_index], (125, 140))
        gameDisplay.blit(images['Play Button Up'], (10, 10))

        button(action = 'Play')

        pygame.display.update()
        clock.tick(FPS)

def main_game():
    gameExit = False
    gameOver = False

    player_x = 0
    player_y = 0

    while not gameExit:

        if gameOver == True:
            gameDisplay.blit(images['Game Over'], 50, 50)
            pygame.display.update()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()



        gameDisplay.blit(images['Background'], (0, 0))
        gameDisplay.blit(images['Starting Game'], (0, 0))
        gameDisplay.blit(images['Base Ground'], (base_x, base_y))
        gameDisplay.blit(images['Player'][player_index], (player_x, player_y))

        pygame.display.update()
        clock.tick(FPS)


main()
Neil Smith
  • 67
  • 5
  • Do you already know how object-oriented programming works? Also, always try to reduce your code to the [minimum](https://stackoverflow.com/help/mcve) before you submit it here, but make sure that it's still complete and runnable. – skrx Sep 28 '17 at 10:02

2 Answers2

2

Here's a minimal example to demonstrate how you can switch between different button images.

When the user presses a mouse button (a pygame.MOUSEBUTTONDOWN event is added to the queue), check if the event.pos collides with the button rect and if it collides, set the image to the "down" version.

When the user releases the button (pygame.MOUSEBUTTONUP event), just set the image back to the original version.

import pygame as pg


pg.init()
screen = pg.display.set_mode((640, 480))

GRAY = pg.Color('gray15')
BLUE = pg.Color('dodgerblue1')
LIGHTBLUE = pg.Color('lightskyblue1')

BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(LIGHTBLUE)


def main():
    clock = pg.time.Clock()
    font = pg.font.Font(None, 30)

    # Currently selected button image.
    button_image = BUTTON_UP_IMG
    button_rect = button_image.get_rect(topleft=(200, 200))
    x = 0

    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            if event.type == pg.MOUSEBUTTONDOWN:
                if event.button == 1:
                    if button_rect.collidepoint(event.pos):
                        button_image = BUTTON_DOWN_IMG
            elif event.type == pg.MOUSEBUTTONUP:
                if event.button == 1:
                    button_image = BUTTON_UP_IMG
                    if button_rect.collidepoint(event.pos):
                        print('Button pressed.')
                        x += 1

        screen.fill(GRAY)
        screen.blit(button_image, button_rect)
        txt = font.render(str(x), True, BLUE)
        screen.blit(txt, (260, 206))

        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    main()
    pg.quit()

I would actually recommend to use classes, pygame sprites and sprite groups instead of just rects and images. Then you can easily create as many instances of the button class as you want.

import pygame as pg


pg.init()
GRAY= pg.Color('gray12')
BLUE = pg.Color('dodgerblue1')
FONT = pg.font.Font(None, 30)

BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(pg.Color('lightskyblue1'))

# The Button is a pygame sprite, that means we can add the
# instances to a sprite group and then update and render them
# by calling `sprite_group.update()` and `sprite_group.draw(screen)`.
class Button(pg.sprite.Sprite):

    def __init__(self, pos, callback):
        pg.sprite.Sprite.__init__(self)
        self.image = BUTTON_UP_IMG
        self.rect = self.image.get_rect(topleft=pos)
        self.callback = callback

    def handle_event(self, event):
        """Handle events that get passed from the event loop."""
        if event.type == pg.MOUSEBUTTONDOWN:
            if event.button == 1:
                if self.rect.collidepoint(event.pos):
                    self.image = BUTTON_DOWN_IMG
        elif event.type == pg.MOUSEBUTTONUP:
            if event.button == 1:
                self.image = BUTTON_UP_IMG
                if self.rect.collidepoint(event.pos):
                    print('Button pressed.')
                    # Call the function that we passed during the
                    # instantiation. (In this case just `increase_x`.)
                    self.callback()


class Game:

    def __init__(self):
        self.screen = pg.display.set_mode((800, 600))
        self.clock = pg.time.Clock()

        self.x = 0
        self.buttons = pg.sprite.Group(
            Button((200, 200), callback=self.increase_x),
            Button((500, 200), callback=self.decrease_x))
        self.done = False

    # A callback function that we pass to the button instance.
    # It gets called if a collision in the handle_event method
    # is detected.
    def increase_x(self):
        """Increase self.x if button is pressed."""
        self.x += 1

    def decrease_x(self):
        """Decrease self.x if button is pressed."""
        self.x -= 1

    def run(self):
        while not self.done:
            self.handle_events()
            self.run_logic()
            self.draw()
            self.clock.tick(30)

    def handle_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True

            for button in self.buttons:
                button.handle_event(event)

    def run_logic(self):
        self.buttons.update()

    def draw(self):
        self.screen.fill(GRAY)
        self.buttons.draw(self.screen)
        txt = FONT.render(str(self.x), True, BLUE)
        self.screen.blit(txt, (360, 206))

        pg.display.flip()


if __name__ == "__main__":
    Game().run()
    pg.quit()
skrx
  • 19,980
  • 5
  • 34
  • 48
-1

Thanks for the Answer to my previous question your answer solved my previous issue, but now I have ran into another issue.

When I press my button on the main menu it changes sprites correctly and loads the main game correctly, but as soon as I move my mouse it switches back to the main menu. My best guess is that it is because it is looping the whole button sequence, but I am not entirely sure.

import pygame

pygame.init()

display_width = 288
display_height = 512

def main_menu():
    done = False

    play_button_image = images['Play Button Up']
    play_button_rect = play_button_image.get_rect(topleft=(30,15))

    while not done:

        for event in pygame.event.get():

            if event.type == pygame.QUIT:
                done = True
                pygame.quit()
                quit()

            if event.type == pygame.MOUSEBUTTONDOWN:

                if event.button == 1:

                    if play_button_rect.collidepoint(event.pos):
                        play_button_image = images['Play Button Down']

            elif event.type == MOUSEBUTTONUP:

                if event.button == 1:
                    play_button_image = images['Play Button Up']

                    if play_button_rect.collidepoint(event.pos):
                        main_game()

            gameDisplay.blit(play_button_image, play_button_rect)

            pygame.display.update()
            clock.tick(FPS)
            
def main_game():
    gameExit = False
    gameOver = False

    player_index = 0
    player_x = int(display_width * 0.2)
    player_y = int((display_height - images['Player'][0].get_height()) / 2)
    player_index_gen = cycle([0, 1, 2, 1])

    loop_iter = 0

    starting_game_x = int((display_width - images['Starting Game'].get_width()) / 2)
    starting_game_y = int(display_height * 0.12)

    base_x = 0

    base_shift = images['Base Ground'].get_width() - images['Background'].get_width()

    player_move_vals = {'val': 0, 'dir': 1}

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.MOUSEBUTTONDOWN or pygame.K_SPACE or pygame.K_UP:
                sounds['Wing']
                return {
                    'player_y': player_y + player_move_vals['val'],
                    'base_x': base_x,
                    'player_index_gen': player_index_gen
                }

        if (loop_iter + 1) % 5 == 0:
            player_index = next(player_index_gen)
        loop_iter = (loop_iter + 1) % 30
        base_x = -((-base_x + 4) % base_shift)
        player_move(player_move_vals)

        # draw sprites
        gameDisplay.blit(images['Background'], (0, 0))
        gameDisplay.blit(images['Player'][player_index],
                    (player_x, player_y + player_move_vals['val']))
        gameDisplay.blit(images['Starting Game'], (starting_game_x, starting_game_y))
        gameDisplay.blit(images['Base Ground'], (base_x, base_y))

        pygame.display.update()
        clock.tick(FPS)
        
if __name__ == '__main__':
    main()
       
Neil Smith
  • 67
  • 5
  • Please don't post questions as answers. Just delete this answer and post it as a new question, and also tell us what you've tried to solve the problem. I'm already analyzing the code. – skrx Sep 29 '17 at 10:36
  • Take a look at [these answers](https://stackoverflow.com/questions/4986756/why-is-this-string-always-the-highest-possible-number). You need to check `if event.type == pg.KEYDOWN:` and then in this clause `if event.key in (pg.K_SPACE, pg.K_UP):`. – skrx Sep 29 '17 at 18:57