如何有效地掌握Pygame中的密钥?

时间:2014-02-28 11:17:12

标签: python input key pygame keydown

[编辑]我已经在那里发布了解决方案。 [/编辑]

我发现了两个相关的问题:

但我想具体一点。怎么样?

while not done:
    for e in event.get():
        if e.type == KEYDOWN:
            keys = key.get_pressed()
            if e.type == QUIT or keys[K_ESCAPE]:
                done = True
            if keys[K_DOWN]:
                print "DOWN"

当我按向下箭头时,它会打印,但它只打印一次。如果我想再打印一次,我需要再次按下它。

如果我改为使用while关键字,

while keys[K_DOWN]:
    print "DOWN"

由于一些不明原因,我得到了一个无限循环。

这种合乎逻辑的选择也没用:

if ((e.type == KEYDOWN) and keys[K_DOWN]):
    print "DOWN"

还有另一个以某种方式清理事件,你可以使用while:

while not done:
    for e in event.get():
        if e.type == KEYDOWN:
            keys = key.get_pressed()
            if e.type == QUIT or keys[K_ESCAPE]:
                done = True
            while keys[K_DOWN]:
                print "DOWN"
                event.get()
                keys = key.get_pressed()

但是按下向下键不到一秒钟,它会打印数千次。 (移动一个玩家是不可能的,为此调整时钟似乎不是处理它的正确方法(而且我已经尝试过,而且我已经失败了。)。)。

按下并执行该块数千次是没用的。我想要的是按下键并在我没有释放它的情况下继续操作,在规定的游戏时钟速度内。

7 个答案:

答案 0 :(得分:5)

请勿混淆event.get()key.get_pressed()


如果您按下或释放某个键,则会将事件放入您使用event.get()查询的事件队列中。如果您真正感兴趣的是,如果一个键被物理按下或释放(这些是实际的键盘事件),请执行此操作。请注意,KEYDOWN会根据键重复设置多次添加到队列中。

此外,在处理KEYDOWN事件时无需查询所有键的状态,因为您已经知道通过选中event.key

来按下哪个键

如果您对按键是否感兴趣(并忽略您可能想要的按键重复)感兴趣,那么您应该只使用key.get_pressed()。使用一堆标志是不必要的,会使代码混乱。

所以你的代码可以简化为:

while not done: 
    keys = key.get_pressed() 
    if keys[K_DOWN]: 
        print "DOWN" 
    for e in event.get(): 
        pass # proceed other events. 
             # always call event.get() or event.poll() in the main loop

答案 1 :(得分:2)

我不熟悉Pygame,但是,正如我所看到的,其中的程序应该具有基于事件的架构。除非你得到传入的事件并处理它们,否则没有任何反应。这就是为什么你的简单循环变得无限:它只是不处理事件。

while keys[K_DOWN]: # Nobody updates the keys, no events are processed
    print "DOWN"

然后关于get_pressed()电话。它返回的是一个键列表。所以,你试图循​​环直到密钥被释放。那是个问题。根据{{​​3}},即使队列中没有事件,pygame.event.get()也会立即返回。对get()的调用意味着:我的代码仍然有用,但我不想阻止事件,所以请在继续之前处理待处理的事件。如果您的代码只是在等待某个事件,那就意味着它无关。

事件的WAIT函数(不阻塞Pygame的内部循环)是pygame.event.wait()(逻辑是:我的代码中没有任何事情要做,直到发生了什么)。但是,如果您使用它,则必须获取有关从事件本身按下或释放的键的信息,而不是get_pressed()

以下是对文档页面的评论中的示例:

for event in pygame.event.get() :
  if event.type == pygame.KEYDOWN :
    if event.key == pygame.K_SPACE :
      print "Space bar pressed down." #Here you should put you program in the mode associated with the pressed SPACE key
    elif event.key == pygame.K_ESCAPE :
      print "Escape key pressed down."
  elif event.type == pygame.KEYUP :
    if event.key == pygame.K_SPACE :
      print "Space bar released."
    elif event.key == pygame.K_ESCAPE :
      print "Escape key released." #Time to change the mode again

答案 2 :(得分:1)

我喜欢采用稍微不同的方法解决这个问题。不是检查按键是否被按下并在按下时采取某些动作,而是在按键上设置一个标志并在按键上取消设置。然后在更新玩家位置的功能中,检查标志并相应地更新。以下伪Python解释了我的目标:

if down_key_pressed:
    down_flag = True
elif down_key_released:
    down_flag = False
elif right_key_pressed:
    etc...

这应该在一个单独的循环中完成,该循环接受玩家的输入。然后在update_player_position()中你可以这样做:

if down_flag:
    move_player_down()
elif right_flag:
    move_player_right()

此示例假定为四向移动,但您只需使用if down_flag and right_flag即可轻松将其扩展为八向。

答案 3 :(得分:0)

[编辑] Dominik的解决方案是完美的解决方案(从键盘解决方案中分离event.get())。它完美地运作!!最后,没有pygame输入的问题。[/ EDIT]

标志:

flag = False # The flag is essential.
while not done:
    for e in event.get(): # At each frame it loops through all possible events.
        keys = key.get_pressed() # It gets the states of all keyboard keys.
        if e.type == QUIT:
            done = True
        if e.type == KEYDOWN: # If the type is KEYDOWN (DIFFERENT FROM "HELD").
            if keys[K_DOWN]: # And if the key is K_DOWN:
                flag = True # The flag is set to true.
        elif e.type == KEYUP: # The very opposite.
            if keys[K_DOWN]:
                flag = False
    if flag == True: # DON'T USE "while flag == true", it'll crash everything. At every frame, it'll check if the flag is true up there. It's important to remember that the KEYDOWN worked ONLY ONCE, and it was enough to set that flag. So, at every frame, THE FLAG is checked, NOT THE KEYDOWN STATE. Every "player movement" while a key is being held MUST be done through flags.
        print "DOWN"

答案 4 :(得分:0)

我正在使用不同的方法来按住我在向左或向右移动对象的特定任务中使用的键。

我不知道计算机知道密钥实际上已被按下。

当我按下某个键时,我为该键定义的变量(EG:左箭头)设置为True。 直到该键未被按下(使用事件pygame.KEYUP)"向左移动"执行。

答案 5 :(得分:-1)

如果您使用keydown为每个关键事件设置时间限制,则可以重复获取pygame.key.set_repeat(# millisecond)事件。 Quote:当启用键盘重复时,按下的键将生成多个pygame.KEYDOWN事件。延迟是第一次重复发送pygame.KEYDOWN之前的毫秒数。之后,每隔一个毫秒发送另一个pygame.KEYDOWN。如果没有传递参数,则禁用密钥重复。 初始化pygame时,密钥重复被禁用。请参阅以下链接了解详细信息http://www.pygame.org/docs/ref/key.html#pygame.key.set_repeat

答案 6 :(得分:-2)

这基本上是我如何做到的,那么请访问www.cswonders.com,它可能会对你有所帮助。转到第3级,然后是3.6,并完成教程。我是从那里学到的。这是我的代码。

def update(self):
    self.speedx = 0
    self.speedy = 0
    # If left or right key is pressed, move left or right
    pressed_key = pygame.key.get_pressed()
    if pressed_key[pygame.K_LEFT]: 
        self.speedx = -10          
    if pressed_key[pygame.K_RIGHT]:
        self.speedx = 10
    if pressed_key[pygame.K_UP]:
        self.speedy = -10
    if pressed_key[pygame.K_DOWN]:
        self.speedy = 10

    self.rect.x += self.speedx
    self.rect.y += self.speedy

    # No further move if off screen
    if self.rect.right > SCREEN_WIDTH:
        self.rect.right = SCREEN_WIDTH
    if self.rect.left < 0:
        self.rect.left = 0