Как сделать плавное движение в pygame

#python #pygame

#python #pygame

Вопрос:

Мы с моим другом только начинаем учиться программировать с pygame на repl.it и для нашего первого «настоящего» проекта мы хотим создать олдскульное приключение, похожее на point’n’click.

Однако у нас есть проблема с перемещением персонажа, если мы щелкнем где-нибудь на экране, персонаж просто «телепортируется» туда, но мы хотим, чтобы это выглядело как можно более плавно.

Итак, по сути, мы хотим избавиться от «телепортации» персонажа и вместо этого получить плавный покадровый переход от текущего положения персонажа к положению мыши.

Мы уже пытались замедлить циклы 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))
  

Комментарии:

1. Кроме того, вы можете проверить щелчок мыши, подписав первое значение в кортеже if mstate[0]: do stuff .

2. Кроме того, вы можете выполнить screen.blit(pmc, (x2-46, y2-184)) только один раз в основном цикле, за пределами операторов while and if , которые вы показываете в вопросе

Ответ №1:

У вас есть игровой цикл, так что используйте его. Просто переместите персонажа на определенную позицию в каждом кадре. Например, перемещать символ на кадр 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_x , follower_y ) до ( mainsprite_x , mainsprite_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.
  

Определите a maximum_distance и a 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. вау, это действительно хорошее решение для этого, большое спасибо. Хотя это немного сложно для нас из-за наших текущих знаний о phython: P.

2. @esms10 Все, что вам нужно сделать, это скопировать функцию FollowMe и вызвать x, y = FollowMe((x, y), (x2, y2)) .

Ответ №2:

Я бы не рекомендовал использовать 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)

Комментарии:

1. О, да, это немного смущает, что мы сами этого не заметили, но спасибо, чувак: P.

2. и мы используем repl.it потому что это единственный известный нам онлайн-компилятор, который довольно удобен для пользователя, потому что мы только начали кодировать, поэтому мы в принципе ничего не знаем: P.