Pygame: key.get_pressed() does not coincide with the event queue

前端 未结 4 442
心在旅途
心在旅途 2020-11-30 13:25

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 key

相关标签:
4条回答
  • 2020-11-30 13:32

    You are using event-based input , but in this case you want polling-based input. Then you don't mess with key-repeats.

    import pygame
    from pygame.locals import *
    
    done = False    
    player.pos = Rect(0,0,10,10)
    
    while not done:
        for event in pygame.event.get():
            # any other key event input
            if event.type == QUIT:
                done = True        
            elif event.type == KEYDOWN:
                if event.key == K_ESC:
                    done = True
                elif event.key == K_F1:
                    print "hi world mode"
    
        # get key current state
        keys = pygame.key.get_pressed()
        if keys[K_LEFT]:
            player.pos.left -= 10
        if keys[K_RIGHT]:
            player.pos.left += 10
        if keys[K_UP]:
            player.pos.top -= 10
        if keys[K_DOWN]:
            player.pos.left += 10
        if keys[K_SPACE]: 
            print 'firing repeated gun'
    
    0 讨论(0)
  • 2020-11-30 13:42

    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:

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN:
            if event.key == K_LEFT:
                player.pos = (player.pos[0] - 2, player.pos[1])
    

    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:

    keystates={'up':False, 'down':False, 'left':False, 'right':False}
    running=True
    
    #start main pygame event processing loop here
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running=False
    
            #check for key down events
            if event.type == KEYDOWN:
                if event.key == K_UP:
                    keystates['up']=True
                if event.key == K_DOWN:
                    keystates['down']=True
                if event.key == K_LEFT:
                    keystates['left']=True
                if event.key == K_RIGHT:
                    keystates['right']=True
    
            #check for key up events
            if event.type == KEYUP:
                if event.key == K_UP:
                    keystates['up']=False
                if event.key == K_DOWN:
                    keystates['down']=False
                if event.key == K_LEFT:
                    keystates['left']=False
                if event.key == K_RIGHT:
                    keystates['right']=False
    
        #do something about the key states here, now that the event queue has been processed
        if keystates['up']:
            character.moveUp()  #or whatever your call for these are...
        if keystates['down']:
            character.moveDown()
        if keystates['left']:
            character.moveLeft()
        if keystates['right']:
            character.moveRight()
    
    #gracefully exit pygame here
    pygame.quit()
    
    0 讨论(0)
  • 2020-11-30 13:49

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

    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
    
    pygame.init()
    
    screen = pygame.display.set_mode((200, 200))
    run = True
    pos = pygame.Vector2(100, 100)
    clock = pygame.time.Clock()
    
    # speed of your player
    speed = 2
    
    # key bindings
    move_map = {pygame.K_LEFT: pygame.Vector2(-1, 0),
                pygame.K_RIGHT: pygame.Vector2(1, 0),
                pygame.K_UP: pygame.Vector2(0, -1),
                pygame.K_DOWN: pygame.Vector2(0, 1)}
    
    while run:
      for e in pygame.event.get():
        if e.type == pygame.QUIT: run = False
    
      screen.fill((30, 30, 30))
      # draw player, but convert position to integers first
      pygame.draw.circle(screen, pygame.Color('dodgerblue'), [int(x) for x in pos], 10)
      pygame.display.flip()
    
      # determine movement vector
      pressed = pygame.key.get_pressed()
      move_vector = pygame.Vector2(0, 0)
      for m in (move_map[key] for key in move_map if pressed[key]):
        move_vector += m
    
      # normalize movement vector if necessary
      if move_vector.length() > 0:
        move_vector.normalize_ip()
    
      # apply speed to movement vector
      move_vector *= speed
    
      # update position of player
      pos += move_vector
    
      clock.tick(60)
    
    0 讨论(0)
  • 2020-11-30 13:54

    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:

    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.moving_left = True
            if pygame.key.get_pressed()[K_RIGHT]:
                player.moving_right = True
            if pygame.key.get_pressed()[K_UP]:
                player.moving_up = True
            if pygame.key.get_pressed()[K_DOWN]:
                player.moving_down = True
        elif event.type == KEYUP:
            if pygame.key.get_pressed()[K_LEFT]:
                player.moving_left = False
            if pygame.key.get_pressed()[K_RIGHT]:
                player.moving_right = False
            if pygame.key.get_pressed()[K_UP]:
                player.moving_up = False
            if pygame.key.get_pressed()[K_DOWN]:
                player.moving_down = False
    
    # Somewhere else in your game loop...
    if player.moving_left:
        player.pos[0] -= 2
    if player.moving_right:
        player.pos[0] += 2
    if player.moving_up:
        player.pos[1] -= 2
    if player.moving_right:
        player.pos[1] += 2
    
    0 讨论(0)
提交回复
热议问题