Skip to content Skip to sidebar Skip to footer

Pygame, Best Way To Implement Buttons?

I am working on a pygame project with a group where we need to be able to click a button to select a class of objects, and then have that object be placed down on to a grid. It is

Solution 1:

To make this work, let's think about what we need.

We need something to represent a button: a button has a text, and an action that is invoked when we click it.

Since something happens when a button is clicked, we need to somehow represent the fact that the game has different game states: let's describe them as 'the game is running and buttons can be pressed' and 'the player has to select a position for something'.

Here's how it could look like. Note the comments (I assume basic pygame knowledge of how the Surface, Rect and Sprite classes etc. work).

import pygame
import random

# this class is a container for # our game state and all the spritesclassGame:
    def__init__(self, font):
        self.font = font
        # currently we have 2 states: RUNNING and SELECT_POSITION
        self.state = 'RUNNING'# a sprite group for all sprites (+ UI)
        self.sprites = pygame.sprite.Group()

        # a sprite group for all game objects (- UI)
        self.actors = pygame.sprite.Group()
        self.callback = Nonedefupdate(self, events, dt):
        for event in events:
            if event.type == pygame.MOUSEBUTTONDOWN:

                # if we're in SELECT_POSITION, a mouse click will# change the game state back to RUNNING# we pass the mouse position to the callback# so the action actually happensif self.state == 'SELECT_POSITION'and self.callback:
                    self.callback(event.pos)
                    self.state = 'RUNNING'# just update the sprites
        self.sprites.update(events, dt)

    defdraw(self, screen):
        # usually, the background is black, but to give the player # a visual clue that they have to do something, let's change# it to grey when we're in the SELECT_POSITION mode
        screen.fill(pygame.Color('black'if self.state == 'RUNNING'else'grey'))

        # just draw all the sprites
        self.sprites.draw(screen)

        # just some info text for the playerif self.state == 'SELECT_POSITION':
            screen.blit(self.font.render('Select a position', True, pygame.Color('black')), (150, 400))

    # a button can call this function when the action that should be invoked# needs a position that the player has to choosedefselect_position(self, callback):
        self.state = 'SELECT_POSITION'
        self.callback = callback

# just a little square guy that walks around the screen# nothing special happens hereclassWalkingRect(pygame.sprite.Sprite):
    def__init__(self, pos, color, game):
        super().__init__(game.sprites, game.actors)
        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color(color))
        self.rect = self.image.get_rect(center=pos)
        self.pos = pygame.Vector2(pos)
        self.direction = pygame.Vector2(random.choice([-1,0,1]), random.choice([-1,1])).normalize()

    defupdate(self, events, dt):
        self.pos += self.direction * dt/10
        self.rect.center = self.pos
        if random.randint(0, 100) < 10:
            self.direction = pygame.Vector2(random.choice([-1,0,1]), random.choice([-1,1])).normalize()

# the actuall Button class# it takes an action that is invoked when the player clicks itclassButton(pygame.sprite.Sprite):
    def__init__(self, pos, color, text, game, action):
        super().__init__(game.sprites)
        self.color = color
        self.action = action
        self.game = game
        self.text = text
        self.image = pygame.Surface((150, 40))
        self.rect = self.image.get_rect(topleft=pos)
        self.fill_surf(self.color)

    deffill_surf(self, color):
        self.image.fill(pygame.Color(color))
        self.image.blit(self.game.font.render(self.text, True, pygame.Color('White')), (10, 10))

    defupdate(self, events, dt):

        # the player can only use the button when the game is in the RUNNING stateif self.game.state != 'RUNNING':
            self.fill_surf('darkgrey')
            return

        self.fill_surf(self.color)
        for event in events:
            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.rect.collidepoint(event.pos):
                    # if the player clicked the button, the action is invoked
                    self.action(self.game)

defmain():
    pygame.init()
    screen = pygame.display.set_mode((500, 500))
    screen_rect = screen.get_rect()
    font = pygame.font.SysFont(None, 26)
    clock = pygame.time.Clock()

    game = Game(font)

    # the action for the green button# when invoked, as the game for the player# to select a position, and spawn a green# guy at that positiondefgreen_action(game_obj):
        defcreate_green(pos):
            WalkingRect(pos, 'green', game_obj)
        game_obj.select_position(create_green)

    # the same but spawn a red guy insteaddefred_action(game_obj):
        defcreate_red(pos):
            WalkingRect(pos, 'darkred', game_obj)
        game_obj.select_position(create_red)

    Button((10, 10), 'green', 'CREATE GREEN', game, green_action)
    Button((10, 50), 'darkred', 'CREATE RED', game, red_action)

    # a button to kill all guys# just to show how generic our buttons are
    Button((10, 90), 'red', 'KILL', game, lambda game_obj: [x.kill() for x in game_obj.actors])

    # classic boring main loop# just updates and draws the game
    dt = 0whileTrue:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        game.update(events, dt)
        game.draw(screen)

        pygame.display.flip()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Post a Comment for "Pygame, Best Way To Implement Buttons?"