如何在pygame中实现平滑移动

3
我和一个朋友在repl.it上开始学习pygame编程。我们想制作一款像老式的指针式冒险游戏那样的“真正”项目,但是我们遇到了人物移动的问题。如果我们在屏幕上的某个地方点击鼠标,人物就会“瞬间传送”到那里,但我们希望它看起来尽可能平滑。
因此,我们希望消除人物“瞬间传送”的问题,并改为将人物从当前位置以逐帧过渡到鼠标位置。
我们已经尝试减慢while循环的速度,这样每次执行while循环时都可以将人物投影出来,但这会导致整个网站崩溃。我们还尝试在repl.it之外进行操作,以防止该网站存在问题,但那里也没有解决问题。
#PMC = Character
#mpos = the mouse position 
#mstate= the state of the mouse buttons (0 if nothing is pressed, 1 if a mouse 
#button is pressed) 
#charspeed = the speed at which the character moves (=1px)
  ```
#---PMC movement when mouse click-----------------------
    #---x,y = mpos   x2,y2 = characterpos
    if mstate == (1,0,0):
      #print('x: ', x, ' y: ', y, '   x2: ', x2, ' y2: ', y2) #debugging_positions

      
      while x2 != x:
        if x2>x:
          x2-=charspeed
          screen.blit(pmc, (x2-46, y2-184))
        if x2<x:
          x2+=charspeed
          screen.blit(pmc, (x2-46, y2-184))
          
      while y2 != y:
        if y2>y:
          y2 -= charspeed
          screen.blit(pmc, (x2-46, y2-184))
        if y2<y:
          y2 += charspeed
          screen.blit(pmc, (x2-46, y2-184))

此外,您可以通过对元组中的第一个值进行下标操作来检查鼠标单击事件,例如 if mstate[0]: do stuff - user12291970
此外,在主循环之外,您可以只执行一次screen.blit(pmc, (x2-46, y2-184)),而不是在您所展示的问题中的whileif语句中执行。 - user12291970
2个回答

3
你有一个游戏循环,所以要使用它。只需在每个帧中将角色移动一定位置。例如,每帧移动角色step
step = 1

if x2 + step <= x:
    x2 += step
elif x2 - step >= x:
    x2 -= step
else:
    x2 = x

if y2 + step <= y:
    y2 += step
elif y2 - step >= y:
    y2 -= step
else:
    y2 = y

对于更复杂的解决方案,您需要计算点到目标的欧几里得距离。使用pygame.math.Vector2进行计算。
计算跟随者和精灵之间的距离以及从(follower_xfollower_y)到(mainsprite_xmainsprite_y)的单位方向向量。可以通过将方向向量除以距离或通过对方向向量进行归一化(normalize())来计算单位向量
target_vector = Vector2(mainsprite_x, mainsprite_y)
follower_vector = Vector2(follower_x, follower_y)

distance = follower_vector.distance_to(target_vector)
direction_vector = target_vector - follower_vector
if distance > 0:
    direction_vector /= distance

现在,您可以定义一个精确的step_distance并向精灵的跟随者移动。
if distance > 0:
    new_follower_vector = follower_vector + direction_vector * step_distance.

定义一个maximum_distance和一个minimum_distance。最小步距为:
min_step = max(0, distance - maximum_distance)

最大步距离为:
max_step = distance - minimum_distance

把它们全部组合起来:
minimum_distance    = 0
maximum_distance    = 10000
target_vector       = Vector2(mainsprite_x, mainsprite_y)
follower_vector     = Vector2(follower_x, follower_y)
new_follower_vector = Vector2(follower_x, follower_y)

distance = follower_vector.distance_to(target_vector)
if distance > minimum_distance:
    direction_vector    = (target_vector - follower_vector) / distance
    min_step            = max(0, distance - maximum_distance)
    max_step            = distance - minimum_distance
    step_distance       = min_step + (max_step - min_step) * LERP_FACTOR
    new_follower_vector = follower_vector + direction_vector * step_distance

最简示例: repl.it/@Rabbid76/PyGame-FollowMouseSmoothly

import pygame

LERP_FACTOR      = 0.05
minimum_distance = 25
maximum_distance = 100

def FollowMe(pops, fpos):
    target_vector       = pygame.math.Vector2(*pops)
    follower_vector     = pygame.math.Vector2(*fpos)
    new_follower_vector = pygame.math.Vector2(*fpos)

    distance = follower_vector.distance_to(target_vector)
    if distance > minimum_distance:
        direction_vector    = (target_vector - follower_vector) / distance
        min_step            = max(0, distance - maximum_distance)
        max_step            = distance - minimum_distance
        step_distance       = min_step + (max_step - min_step) * LERP_FACTOR
        new_follower_vector = follower_vector + direction_vector * step_distance

    return (new_follower_vector.x, new_follower_vector.y) 

pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()

follower = (100, 100)
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    player   = pygame.mouse.get_pos()
    follower = FollowMe(player, follower)

    window.fill(0)  
    pygame.draw.circle(window, (0, 0, 255), player, 10)
    pygame.draw.circle(window, (255, 0, 0), (round(follower[0]), round(follower[1])), 10)
    pygame.display.flip()

1
哇,那真是一个非常好的解决方案,非常感谢。 虽然对于我们目前对Python的了解来说有点复杂 :P。 - esms10
@esms10,你所要做的就是复制函数 FollowMe 并调用 x, y = FollowMe((x, y), (x2, y2)) - Rabbid76

0

我不建议使用repl.it,因为它往往运行非常缓慢。

此外,您的代码应该看起来更像这样:

    while True:
        screen.fill((0,0,0))

        stuff happens

        if x2>x:
            x2-=charspeed
        elif x2<x:
            x2+=charspeed
        elif y2>y:
            y2 -= charspeed
        elif y2<y:
            y2 += charspeed
        screen.blit(pmc, (x2-46, y2-184))
        pygame.display.flip()

直到它完全移动到(x,y)位置之前,您才更新显示。


哦,谢谢你,我们自己没注意到这一点有点尴尬,但还是谢谢你啊 :P。 - esms10
我们正在使用repl.it,因为它是我们所知道的唯一一个相当用户友好的在线编译器,因为我们刚开始编码,所以基本上什么都不知道 :P。 - esms10

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