如何让两个按键同时响应?

2 投票
5 回答
4033 浏览
提问于 2025-04-18 11:35

我现在正在用Python制作一个乒乓球游戏:

这里插入图片描述

但是,假设球朝着粉色的方向飞来,而黄色的小人决定疯狂按他的 ws 键。这样他的挡板会开始移动(这没问题),但粉色的小人就会停下来(这就不太好了)。

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 个回答

0

在我制作的一个游戏中,角色可以向各个方向移动。我设置了一个机制,当你按下某个键时,不是直接改变角色的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()
0

我觉得你需要使用线程,不然程序总是按顺序执行。

看看这个例子:

让两个函数同时运行

试着在每个线程的函数里放一个 while True,这样两个函数就都能对命令做出响应。

0

正如我之前提到的,我使用了 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()
1

经过一些研究,我觉得这其实是SDL事件队列溢出的问题。SDL是一个C语言的事件库,而PyGame的事件系统就是建立在这个库之上的。根据我了解到的,事件队列最多只能处理128个事件,超过这个数量后,新添加的事件就会被丢弃,直到队列被清空。

你的渲染逻辑效率不高,而且你把输入的检查频率和渲染逻辑绑定在一起(每次游戏循环时,你都要重新渲染整个屏幕、球拍和球,然后再检查事件队列,所以你无法比渲染一个完整的画面更快地清空事件队列)。我觉得你需要改善你的渲染逻辑(看看如何只擦除/重绘精灵的一部分,而不是清空整个缓冲区),并通过将渲染放在一个单独的线程中,来把事件检查和渲染分开。

1

我同意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)

撰写回答