5

我正在尝试为使用 Python 中的 pygame 的应用程序制定简单的控件。我已经掌握了基础知识,但我遇到了一个奇怪的墙:我正在使用箭头键来控制我的角色。如果我按住一个箭头键,然后按住另一个箭头键(沿对角线移动),角色会按预期移动。但是,如果我释放我按下的第二个键(同时仍然按住第一个键),即使我仍然按住第一个键,角色也会停止移动。这是我的简单运动代码:

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)

现在,我自然对此感到非常困惑。所以我尝试打印一些行来调试。在主控制循环的顶部,我写道:

print (pygame.key.get_pressed()[K_DOWN], pygame.key.get_pressed()[K_RIGHT])
print pygame.event.get()

...输出一个显示向下和向右箭头键状态的元组,然后显示 pygame 事件队列。我的结果更让我困惑。如果我沿对角线向下和向右移动角色,先按向下键,然后按右键,然后释放右键使其简单地向下移动,角色像以前一样停止移动......但这会打印到外壳上:

(1, 0)
[]

也就是说,当我松开右箭头键并仍然按住向下箭头键时,pygame.key.get_pressed() 明白向下箭头键仍在按住,但事件队列中没有任何内容。

另外,在代码的前面(在控制循环之前)我正在调用

pygame.key.set_repeat(1, 2)

使角色在按住键的同时继续移动。

任何帮助将不胜感激!谢谢 :)

4

4 回答 4

8

对于像移动这样的事情,你不应该检查事件(比如KEYDOWNor KEYUP),但是如果你的移动键被按下(使用get_pressed)检查你的主循环的每次迭代。

在您的代码中,仅当还有KEYDOWN事件时才检查按下的键。


还有一些其他的事情需要考虑:

  • 您应该将键映射和播放器的速度分开,这样以后更改其中任何一个都会更容易。

  • 您应该首先确定一个运动矢量并对其进行归一化,否则,如果您的垂直和水平运动速度为10,您的对角线运动速度将为 ~ 14

工作示例:

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)
于 2012-08-13T07:42:38.427 回答
3

只需使用事件返回数据,而不是尝试轮询,您已经在检查它是否为 keydown 事件类型,现在只需询问 KEY 索引,如下所示:

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

其余代码.....

还可以考虑使用单独的数据结构来存储控件的状态,然后只使用事件来更新该数据结构。这将有助于使控件更加灵活,因为您不会依赖事件队列来使您的角色移动,根据我的经验,这会导致以下问题:一次无法按下两个以上的按钮,以及奇怪的延迟或角色移动的时间问题。所以像:

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()
于 2012-08-13T09:27:44.840 回答
2

您正在使用基于事件的输入,但在这种情况下,您需要基于轮询的输入。然后你就不会弄乱键重复了。

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'
于 2012-08-11T22:12:41.110 回答
1

我的猜测是 set repeat 不会像你想象的那样工作。基本上,在你的第二个键上升后,重复不会发生。这对我来说似乎很有意义:打开一个文本编辑器并按住“A”键。“A”会溢出屏幕。然后,在按住“A”键的同时按“J”键。“A”停止。这是一个典型的按键重复系统。

我不确定使用这种“set_repeat”方法最终是否会奏效。基本上,玩家按下的任何键现在都会“重复”,即使他们点击“开火”或“跳跃”。

作为替代方案,尝试在用户按下或释放时保存状态。不要使用 set_repeat,而是执行以下操作:

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
于 2012-08-11T00:40:59.410 回答