Python Pygame:key.get_pressed()与事件队列不一致
我正在尝试使用Python中的pygame为应用程序设计简单的控件。我已经有了基本的工作,但我遇到了一个奇怪的问题:我用箭头键来控制我的角色。如果我按住一个箭头键,然后按住另一个箭头键(沿对角线移动),角色将按预期移动。但是,如果我释放我按下的第二个键(同时仍按住第一个键),角色将停止移动,即使我仍按住第一个键。这是我的简单动作代码:Python Pygame:key.get_pressed()与事件队列不一致,python,pygame,game-physics,Python,Pygame,Game Physics,我正在尝试使用Python中的pygame为应用程序设计简单的控件。我已经有了基本的工作,但我遇到了一个奇怪的问题:我用箭头键来控制我的角色。如果我按住一个箭头键,然后按住另一个箭头键(沿对角线移动),角色将按预期移动。但是,如果我释放我按下的第二个键(同时仍按住第一个键),角色将停止移动,即使我仍按住第一个键。这是我的简单动作代码: for event in pygame.event.get(): if event.type == QUIT: pygame.quit()
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)
使角色在按住键的同时继续移动
任何帮助都将不胜感激!谢谢:)我猜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
您使用的是基于事件的输入,但在本例中,您需要基于轮询的输入。这样你就不会重复按键了
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'
对于像移动这样的事情,你不应该检查事件(比如
KEYDOWN
或keydup
),但是如果你的移动键被按下(使用),你应该检查主循环的每一次迭代
在代码中,仅当存在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)
只需使用事件返回数据,而不是尝试轮询,您已经在检查它是否为keydown事件类型,现在只需查询键索引,如下所示:
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()