如何让两个按键同时响应?
我现在正在用Python制作一个乒乓球游戏:
但是,假设球朝着粉色的方向飞来,而黄色的小人决定疯狂按他的 w
和 s
键。这样他的挡板会开始移动(这没问题),但粉色的小人就会停下来(这就不太好了)。
Python能同时监听两个按键事件吗?
这是我的代码:
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((1439, 790))
YELLOW = (255, 255, 0)
PINK = (255, 0, 255)
BLUE = (120, 214, 208)
y1, y2 = (0, 0)
circx, circy = (1439//2, 790//2)
diffx, diffy = (15, 15)
pygame.key.set_repeat(1, 10)
while True:
if (0 <= circy <= 780) == False:
diffy*=-1
if circx <= 60 and y1-10 <= circy <= y1+75:
diffx*=-1
if circx >= 1439-60 and y2-10 <= circy <= y2+75:
diffx*=-1
if (0 <= circx <= 1439) == False:
circx, circy = (720, 395)
DISPLAYSURF.fill((0, 0, 0))
pygame.draw.rect(DISPLAYSURF, YELLOW, (50, y1, 10, 75))
pygame.draw.rect(DISPLAYSURF, PINK, (1439-60, y2, 10, 75))
pygame.draw.circle(DISPLAYSURF, BLUE, (circx, circy), 10)
circx+=diffx
circy+=diffy
try:
for event in pygame.event.get():
if event.type == QUIT or event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
pygame.quit()
sys.exit()
if event.key == pygame.K_w:
y1-=15
if event.key == pygame.K_s:
y1+=15
if event.key == pygame.K_UP:
y2-=15
if event.key == pygame.K_DOWN:
y2+=15
except AttributeError:
pass
pygame.display.flip()
如果同时按下两个键,我该如何让它们独立处理呢?
5 个回答
在我制作的一个游戏中,角色可以向各个方向移动。我设置了一个机制,当你按下某个键时,不是直接改变角色的x和y坐标,而是让一个变量,比如moveLeft、moveRight、moveUp或moveDown(根据按下的键不同)变成True。然后你还需要另写一个东西,当松开键的时候把这个变量变回False。接下来在循环中,它会检查每个变量是否为True,然后根据按下的键来移动角色。此外,我不太明白为什么要有Try循环。
你可以把主循环改成这样:
p1moveup = False
p1movedown = False
p2moveup = False
p2movedown = False
while True:
if (0 <= circy <= 780) == False:
diffy*=-1
if circx <= 60 and y1-10 <= circy <= y1+75:
diffx*=-1
if circx >= 1439-60 and y2-10 <= circy <= y2+75:
diffx*=-1
if (0 <= circx <= 1439) == False:
circx, circy = (720, 395)
DISPLAYSURF.fill((0, 0, 0))
pygame.draw.rect(DISPLAYSURF, YELLOW, (50, y1, 10, 75))
pygame.draw.rect(DISPLAYSURF, PINK, (1439-60, y2, 10, 75))
pygame.draw.circle(DISPLAYSURF, BLUE, (circx, circy), 10)
circx+=diffx
circy+=diffy
for event in pygame.event.get():
if event.type == QUIT or event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
p1moveup = True
if event.key == pygame.K_s:
p1movedown = True
if event.key == pygame.K_UP:
p2moveup = True
if event.key == pygame.K_DOWN:
p2movedown = True
if event.type === pygame.KEYUP:
if event.key == pygame.K_w:
p1moveup = False
if event.key == pygame.K_s:
p1movedown = False
if event.key == pygame.K_UP:
p2moveup = False
if event.key == pygame.K_DOWN:
p2movedown = False
if p1moveup == True:
y1-=15
if p1movedown == True:
y1+=15
if p2moveup == True:
y2-=15
if p2movedown == True:
y2+=15
pygame.display.flip()
正如我之前提到的,我使用了 pygame.key.set_repeat(1, 10)
,这样即使按住某个键,事件也会持续发生。
我发现了另一种方法来实现同样的效果,使用 pygame.key.get_pressed()[pygame.DESIRED_KEY]
。
pygame.key.get_pressed()
会返回一个元组,类似下面这样:
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
当某个特定的键被按下时,0
会变成 1
,这样我们就可以用 if pygame.key.get_pressed()[pygame.DESIRED_KEY]
来判断了。
这是你修改后的代码:
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((1439, 790))
YELLOW = (255, 255, 0)
PINK = (255, 0, 255)
BLUE = (120, 214, 208)
y1, y2 = (0, 0)
circx, circy = (1439//2, 790//2)
diffx, diffy = (15, 15)
pygame.key.set_repeat(1, 10)
while True:
if pygame.key.get_pressed()[pygame.K_UP]:
y1-=15
if pygame.key.get_pressed()[pygame.K_DOWN]:
y1+=15
if pygame.key.get_pressed()[pygame.K_w]:
y2-=15
if pygame.key.get_pressed()[pygame.K_s]:
y2+=15
if (0 <= circy <= 780) == False:
diffy*=-1
if circx <= 60 and y1-10 <= circy <= y1+75:
diffx*=-1
if circx >= 1439-60 and y2-10 <= circy <= y2+75:
diffx*=-1
if (0 <= circx <= 1439) == False:
circx, circy = (720, 395)
DISPLAYSURF.fill((0, 0, 0))
pygame.draw.rect(DISPLAYSURF, YELLOW, (50, y1, 10, 75))
pygame.draw.rect(DISPLAYSURF, PINK, (1439-60, y2, 10, 75))
pygame.draw.circle(DISPLAYSURF, BLUE, (circx, circy), 10)
circx+=diffx
circy+=diffy
try:
for event in pygame.event.get():
if event.type == QUIT or event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
pygame.quit()
sys.exit()
except AttributeError:
pass
pygame.display.flip()
经过一些研究,我觉得这其实是SDL事件队列溢出的问题。SDL是一个C语言的事件库,而PyGame的事件系统就是建立在这个库之上的。根据我了解到的,事件队列最多只能处理128个事件,超过这个数量后,新添加的事件就会被丢弃,直到队列被清空。
你的渲染逻辑效率不高,而且你把输入的检查频率和渲染逻辑绑定在一起(每次游戏循环时,你都要重新渲染整个屏幕、球拍和球,然后再检查事件队列,所以你无法比渲染一个完整的画面更快地清空事件队列)。我觉得你需要改善你的渲染逻辑(看看如何只擦除/重绘精灵的一部分,而不是清空整个缓冲区),并通过将渲染放在一个单独的线程中,来把事件检查和渲染分开。
我同意SilasRay的看法,这几乎肯定不是线程阻塞的问题,但如果真的是这样的话:这个方法应该能解决它(所以如果解决不了,那就可能是你的键盘或者Silas提到的SDL层出了问题)。其实,把你的Paddles做成对象是很重要的,所以不妨考虑一下这部分。线程的相关代码在paddlelisten()
这个函数里。
import threading
class Paddle(object):
def __init__(self, posx, color):
self.posx = posx
self.posy = 0
self.color = color
@property
def pos():
return (self.posx, self.posy, 10, 75)
def main():
# the logic for the program
p1_paddle = Paddle(50, YELLOW)
p2_paddle = Paddle(1439-60, PINK)
def paddlelisten(paddle, upkey, downkey):
if event.key == upkey:
paddle.posy -= 15
if event.key == pygame.downkey:
paddle.posy += 15
t1 = threading.Thread(target=lambda: paddlelisten(p1_paddle, pygame.K_w, pygame.K_s))
t2 = threading.Thread(target=lambda: paddlelisten(p2_paddle, pygame.K_UP, pygame.K_DOWN))
for t in [t1, t2]:
t.daemon = True
t.start()
while True:
# game loop
pygame.draw.rect(DISPLAYSURF, p1_paddle.color, p1_paddle.pos)
pygame.draw.rect(DISPLAYSURF, p2_paddle.color, p2_paddle.pos)