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()

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