I'm attempting to work out simple controls for an application using pygame in Python. I have got the basics working, but I'm hitting a weird wall: I am using the arrow keys to control my character. If I hold down one arrow key, then hold down another arrow key (to move diagonally), the character moves as expected. However, if I release the second key that I pressed (while still holding down the first key), the character stops moving, even though I am still holding down that first key. Here is my simple movement code:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if pygame.key.get_pressed()[K_LEFT]:
player.pos = (player.pos[0] - 2, player.pos[1])
if pygame.key.get_pressed()[K_RIGHT]:
player.pos = (player.pos[0] + 2, player.pos[1])
if pygame.key.get_pressed()[K_UP]:
player.pos = (player.pos[0], player.pos[1] - 2)
if pygame.key.get_pressed()[K_DOWN]:
player.pos = (player.pos[0], player.pos[1] + 2)
Now, I was naturally very confused by this. So I tried to print some lines to debug. In the top of the main control loop, I wrote:
print (pygame.key.get_pressed()[K_DOWN], pygame.key.get_pressed()[K_RIGHT])
print pygame.event.get()
...to output a tuple displaying the state of the down and right arrow keys, and then display the pygame event queue. My results baffled me even more. If I move the character diagonally down and right, pressing the down key first and then the right key, then release the right key to make it move simply downward, the character stops moving as before... but this is printed to the shell:
(1, 0)
[]
That is, when I release the right arrow key and still hold down the down arrow key, pygame.key.get_pressed() understands that the down arrow key is still being held down, but there is nothing in the event queue.
Also, earlier in the code (before the control loop) I am invoking
pygame.key.set_repeat(1, 2)
to make the character continue to move while the key is held down.
Any help will be appreciated! Thanks :)
For things like movement, you should not check for events (like
KEYDOWN
orKEYUP
), but check every iteration of your mainloop if your movement keys are pressed (usingget_pressed
).In your code, you check the pressed keys only if there's also a
KEYDOWN
event.There are also some other things to consider:
You should seperate the key-mapping and the speed of your player, so it will be easier later on to change either of this.
You should determine a movement vector and normalize it first, since otherwise, if your vertical and horizontal movement speed is
10
, your diagonal movement speed would be ~14
.Working example:
My guess is that set repeat doesn't work the way that you think it will. Basically, after your second key goes up, the repeat doesn't happen. This would seem to make sense to me: open up a text editor and hold down the "A" key. "A"s will spill out across the screen. Then, press the "J" key with the "A" key still held down. The "A"s stop. That is a typical key repeat system.
I'm not sure using this "set_repeat" method is going to work out in the end anyway. Basically, any key that the player presses will now "repeat", even if they click "fire" or "jump".
As an alternative, try saving the state when the user presses or releases. Don't use the set_repeat, but do something like the following:
just use the events return data, instead of trying to poll, you're already checking if its a keydown event TYPE, now just interrogate the KEY index, like so:
rest of code.....
also consider using a separate data structure to store the state of your controls, then just use the events to update that data structure. That will help make the controls a bit more flexible as you wont be relying on the event queue to cause your character to move, which in my experience causes problems like: not being able to push more than two buttons at a time, and odd delay or timing issues with character movements. so something like:
You are using event-based input , but in this case you want polling-based input. Then you don't mess with key-repeats.