Pygame中平滑的键盘移动

7

我正在使用以下代码,使得一个玩家角色可以在按下箭头键时在屏幕上移动:

import pygame, sys, time
from pygame.locals import *

pygame.init()

FPS=30
fpsClock=pygame.time.Clock()

width=400
height=300
DISPLAYSURF=pygame.display.set_mode((width,height),0,32)
pygame.display.set_caption('Animation')
background=pygame.image.load('bg.png')


UP='up'
LEFT='left'
RIGHT='right'
DOWN='down'

sprite=pygame.image.load('down.png')
spritex=200
spritey=130
direction=DOWN


pygame.mixer.music.load('bgm.mp3')
pygame.mixer.music.play(-1, 0.0)
while True:
    DISPLAYSURF.blit(background,(0,0))

    DISPLAYSURF.blit(sprite,(spritex,spritey))

    for event in pygame.event.get():
        if event.type==QUIT:
            pygame.quit()
            sys.exit()

        if event.type == KEYDOWN:
            if (event.key == K_LEFT):
                spritex-=5
                sprite=pygame.image.load('left.png')
            elif (event.key == K_RIGHT):
                spritex+=5
                sprite=pygame.image.load('right.png')
            elif (event.key == K_UP):
                spritey-=5
                sprite=pygame.image.load('up.png')
            elif (event.key == K_DOWN):
                spritey+=5
                sprite=pygame.image.load('down.png')

    pygame.display.update()
    fpsClock.tick(FPS)

图片能够移动,但仅在按键时移动5个像素。我希望当按键按下时,图片可以持续移动(同时添加基本窗口碰撞检测,但这是另一个问题)。如何让图片在按键按下时持续移动?


2
我建议使用4个空格缩进而不是制表符。 - jgritty
4个回答

7
我建议使用变量来跟踪哪些箭头键被按下,哪些未被按下。您可以使用 KEYDOWN KEYUP 事件来更新变量。然后,您可以根据按下的键调整精灵每帧的位置。这也意味着您可以通过改变每帧移动的距离来轻松设置精灵在不同方向上的速度。
编辑:
或者如@ monkey所建议的那样,您也可以使用 key.get_pressed()
这是一个未经测试的示例:
while True:
    DISPLAYSURF.blit(background,(0,0))

    DISPLAYSURF.blit(sprite,(spritex,spritey))

    for event in pygame.event.get():
        if event.type==QUIT:
            pygame.quit()
            sys.exit()

        if event.type == KEYDOWN:
            if (event.key == pygame.K_LEFT):
                sprite=pygame.image.load('left.png')
            elif (event.key == pygame.K_RIGHT):
                sprite=pygame.image.load('right.png')
            elif (event.key == pygame.K_UP):
                sprite=pygame.image.load('up.png')
            elif (event.key == pygame.K_DOWN):
                sprite=pygame.image.load('down.png')

    keys_pressed = pygame.key.get_pressed()

    if keys_pressed[pygame.K_LEFT]:
        spritex -= 5

    if keys_pressed[pygame.K_RIGHT]:
        spritex += 5

    if keys_pressed[pygame.K_UP]:
        spritey -= 5

    if keys_pressed[pygame.K_DOWN]:
        spritey += 5

    pygame.display.update()
    fpsClock.tick(FPS)

6
如果您使用 key.get_pressed(),则不需要为每个键创建额外的变量。您可以通过逻辑判断 pressed[K_LEFT] 是否为真来执行操作。 - ninMonkey
1
OP每次只能朝一个基本方向移动,但是你的解决方案可以在按下多个按钮时同时朝多个方向移动。 - Sam Mussmann
@Sam Mussmann:我认为需要多个方向,但是可以通过使用单个if语句来调整精灵的位置(elif keys_pressed...而不是if keys_pressed...)来轻松更改。 - grc
我喜欢这个解决方案比我的更好。我甚至没有考虑过对角线移动。 - jgritty

2
我建议使用set_repeat函数。按住键会定期生成多个事件(由函数的参数设置)。这使您可以无需修改代码(无需额外变量)即可使用代码。
函数原型:
set_repeat(delay, interval)
第一个参数delay是在发送第一个重复的pygame.KEYDOWN之前等待的毫秒数。之后,每隔interval毫秒就会发送另一个pygame.KEYDOWN。如果没有传递参数,则禁用键重复。
只需在主循环之前使用此函数即可。
 pygame.key.set_repeat(10,10)

来源:http://www.pygame.org/docs/ref/key.html#pygame.key.set_repeat

这个函数可以设置键盘重复按键的延迟和频率。在用户按住键盘上某个键不放时,如果键盘重复按键功能开启,那么该键就会开始重复输入。该函数接受两个参数:delay 和 interval。delay 参数表示用户按下键后多少毫秒才开始重复输入,interval 参数表示每次重复输入之间的时间间隔是多少毫秒。


谢谢 - 这个解决方案实现起来简单多了。 - jm22b

1
我是这样做的:
import pygame, sys, time
from pygame.locals import *

pygame.init()

FPS=30
fpsClock=pygame.time.Clock()

width=400
height=300
DISPLAYSURF=pygame.display.set_mode((width,height),0,32)
pygame.display.set_caption('Animation')
background=pygame.image.load('bg.png')


UP='up'
LEFT='left'
RIGHT='right'
DOWN='down'

sprite=pygame.image.load('down.png')
spritex=200
spritey=130
direction=None

def move(direction, sprite, spritex, spritey):
    if direction:
        if direction == K_UP:
            spritey-=5
            sprite=pygame.image.load('up.png')
        elif direction == K_DOWN:
            spritey+=5
            sprite=pygame.image.load('down.png')
        if direction == K_LEFT:
            spritex-=5
            sprite=pygame.image.load('left.png')
        elif direction == K_RIGHT:
            spritex+=5
            sprite=pygame.image.load('right.png')
    return sprite, spritex, spritey

pygame.mixer.music.load('bgm.mp3')
pygame.mixer.music.play(-1, 0.0)
while True:
    DISPLAYSURF.blit(background,(0,0))

    DISPLAYSURF.blit(sprite,(spritex,spritey))

    for event in pygame.event.get():
        if event.type==QUIT:
            pygame.quit()
            sys.exit()

        if event.type == KEYDOWN:
            direction = event.key
        if event.type == KEYUP:
            if (event.key == direction):
                direction = None
    sprite, spritex, spritey = move(direction, sprite, spritex, spritey)

    pygame.display.update()
    fpsClock.tick(FPS)

1
我认为你只需要一个 if (event.key == direction) - Sam Mussmann
另外,如果direction是我们想要的其中之一,难道你不能只设置direction = event.key吗?也许可以使用集合来检查成员资格? - Sam Mussmann
在其他情况下,移动功能不会有任何作用,所以即使没有这个函数也没有关系。 - jgritty
1
虽然如果你按住左箭头键,然后再按下并释放 'a' 键,可能会表现得很奇怪。 - Sam Mussmann
是的,如果你按下其他键,它会停止运动。 - jgritty
我不建议每次重新加载图像。最好的做法是加载并存储它们一次,然后根据需要使用它们。 - underscoreC

0

我会通过在循环内部采取稍微不同的行动来实现这一点。

为了更新精灵,我首先会检查是否有KEYDOWN事件,然后根据该事件设置direction。然后,我会基于direction更新spritespritexspritey。接下来,如果合适的话,我会检查KEYUP事件并根据该事件设置direction

以下是我的编码方式:

sprite=pygame.image.load('down.png')
spritex=200
spritey=130
direction='down'

dir_from_key = {
  K_LEFT: 'left',
  K_RIGHT: 'right',
  K_UP: 'up',
  K_DOWN: 'down'
}

pygame.mixer.music.load('bgm.mp3')
pygame.mixer.music.play(-1, 0.0)
while True:
    DISPLAYSURF.blit(background,(0,0))

    DISPLAYSURF.blit(sprite,(spritex,spritey))

    # Get all the events for this tick into a list
    events = list(pygame.event.get()) 

    quit_events = [e for e in events if e.type == QUIT]
    keydown_events = [e for e in events if e.type == KEYDOWN 
                                           and e.key in dir_from_key]
    keyup_events = [e for e in events if e.type == KEYUP 
                                         and e.key in dir_from_key]

    # If there's no quit event, then the empty list acts like false
    if quit_events:
        pygame.quit()
        sys.exit()

    # Non-last key down events will be overridden anyway
    if keydown_events:
      direction = dir_from_key[keydown_events[-1].key]

    # Change location and image based on direction
    if direction == 'left':
      spritex-=5
      sprite=pygame.image.load('left.png')
    elif direction == 'right':
      spritex+=5
      sprite=pygame.image.load('right.png')
    elif direction == 'up':
      spritey-=5
      sprite=pygame.image.load('up.png')
    elif direction == 'down':
      spritey+=5
      sprite=pygame.image.load('down.png')

   # If there's a keyup event for the current direction.
   if [e for e in keyup_events if dir_from_key[e.key] == direction]:
      direction = None

   pygame.display.update()
   fpsClock.tick(FPS)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接