I want a single key press to result in the sending of one event in my game loop. As a toy example, if the player presses and holds LEFT, I want the sprite to move 1 pixel left and then stop until the player releases the key and presses it again.
Pygame allows you to set the repeat characteristics of key presses using
Not calling this method at all, calling it without parameters, or specifying 0 as its delay parameter are all supposed to result in the behavior I want.
From the documentation:
control how held keys are repeated
set_repeat() -> None
set_repeat(delay, interval) -> None
When the keyboard repeat is enabled, keys that are held down will generate multiple pygame.KEYDOWN events. The delay is the number of milliseconds before the first repeated pygame.KEYDOWN will be sent. After that another pygame.KEYDOWN will be sent every interval milliseconds. If no arguments are passed the key repeat is disabled.
When pygame is initialized the key repeat is disabled.
Unfortunately, I can't get this to work. I tried not calling the method at all, calling the method without parameters, and calling the method with a delay set to 0 (which is, strangely, supposed to disable key repeating -- see the comments in the documentation).
Here's a minimal example:
import pygame as pg import sys # colors WHITE = (255, 255, 255) GREY = (100, 100, 100) class Game: def __init__(self): pg.init() self.screen = pg.display.set_mode((640, 640)) self.clock = pg.time.Clock() pg.key.set_repeat() def new(self): self.all_sprites = pg.sprite.Group() self.player = Player(self, 100,100) def run(self): self.playing = True while self.playing: self.clock.tick(60) self.events() self.update() self.draw() def quit(self): pg.quit() sys.exit() def update(self): self.all_sprites.update() def draw(self): self.screen.fill(GREY) for sprite in self.all_sprites: self.screen.blit(sprite.image, sprite.rect) pg.display.flip() def events(self): for self.event in pg.event.get(): if self.event.type == pg.QUIT: self.quit() class Player(pg.sprite.Sprite): def __init__(self, game, x, y): self.groups = game.all_sprites pg.sprite.Sprite.__init__(self, self.groups) self.game = game self.image = pg.Surface((32,32)) self.image.fill(WHITE) self.rect = self.image.get_rect() self.pos = [x, y] def get_keys(self): if self.game.event.type == pg.KEYDOWN: if self.game.event.key == pg.K_LEFT: self.pos -= 1 print("This should only be printed once per key press") elif self.game.event.key == pg.K_RIGHT: self.game.player.pos += 1 print("This should only be printed once per key press") def update(self): self.get_keys() self.rect.centery = self.pos self.rect.centerx = self.pos g = Game() while True: g.new() g.run()
This does not result in the behavior I want. Pressing the LEFT key will continuously move the sprite left, and continuously print my test message. I have tried calling the method at various other spots in my code (for example, during the events() method of the game class and during the get_keys() method of the player class). No luck.
My question is simple: What am I doing wrong?
For what it's worth, when I call
which is supposed to tell you the repeat characteristics for the current instance of pygame, the value returned is (0,0).
I am working on a MacBook 2010, running macOS Sierra 10.12.6.
I am interested to know if others can replicate this issue.
I know that I can create a workaround using booleans to track keydowns and keyups, but this would be a nuisance (the project relies on a lot of single key presses).