Pygame: key.get_pressed() does not coincide with the event queue
Question: 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 :)


Answer: For things like movement, you should not check for events (like `KEYDOWN` or
`KEYUP`), but check every iteration of your mainloop if your movement keys are
pressed (using
[`get_pressed`](http://www.pygame.org/docs/ref/key.html#pygame.key.get_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:**

    
    
    import pygame
    import math
    
    pygame.init()
    
    screen = pygame.display.set_mode((200, 200))
    run = True
    pos = (100, 100)
    clock = pygame.time.Clock()
    
    # speed of your player
    speed = 2
    
    # key bindings
    move_map = {pygame.K_LEFT: (-1, 0),
                pygame.K_RIGHT: (1, 0),
                pygame.K_UP: (0, -1),
                pygame.K_DOWN: (0, 1)}
    
    while run:
      screen.fill((0, 255, 0))
      # draw player, but convert position to integers first
      pygame.draw.circle(screen, (255, 0, 0), map(int, pos), 10)
      pygame.display.flip()
    
      # determine movement vector
      pressed = pygame.key.get_pressed()
      move_vector = (0, 0)
      for m in (move_map[key] for key in move_map if pressed[key]):
        move_vector = map(sum, zip(move_vector, m))
    
      # normalize movement vector if necessary
      if sum(map(abs, move_vector)) == 2:
        move_vector = [p/1.4142 for p in move_vector]
    
      # apply speed to movement vector
      move_vector = [speed*p for p in move_vector]
    
      # update position of player
      pos = map(sum, zip(pos, move_vector))
    
      for e in pygame.event.get():
        if e.type == pygame.QUIT: run = False
    
      clock.tick(60)
    

