Reputation: 41
A friend of mine and me are just starting to learn to program with pygame on repl.it and for our first "real" project we want to make an old school like point'n'click adventure.
However, we have a problem with the movement of the character, if we click somewhere on the screen the character just "teleports" there but we want it to look as smooth as possible.
So basically, we want to get rid of the "teleporting" of the character and instead have a smooth frame-by-frame transition from the characters current position to the mouse position.
We've already tried to slow down the while loops so that we could project the character each time the while loop is executed but that just crashes the whole site, we also tried to do it outside of repl.it in case it was a problem with the website but it didn't work there either.
#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))
Upvotes: 4
Views: 5854
Reputation: 210899
You have a game loop, so use it. Just move the character by a certain position in each frame. For instance mov the character by step
per frame:
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
For a more sophisticated solution, you've to compute the Euclidean distance form the point to the target. Use pygame.math.Vector2
for the computation.
Compute the distance from between the follower and the sprite and the unit direction vector from (follower_x
, follower_y
) to (mainsprite_x
, mainsprite_y
). The Unit Vector can be computed by dividing the direction vector by the distance or by normalizing (normalize()
) the direction vector:
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
Now you can define an exact step_distance
and move to follower int direction of the sprite:
if distance > 0:
new_follower_vector = follower_vector + direction_vector * step_distance.
Define a maximum_distance
and a minimum_distance
. The minimum step distance is:
min_step = max(0, distance - maximum_distance)
The maximum step distance is
max_step = distance - minimum_distance
Put it all together:
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
Minimal example: 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()
Upvotes: 3
Reputation: 54
I wouldn't recommend using repl.it as it tends to run very slowly.
Also your code should look more like this:
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()
You weren't updating the display until it had moved all of the way to (x,y)
Upvotes: -1