Why position.direction_to() giving opposite values every frame.

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By kamilko

I have such code:

extends KinematicBody2D

var new_position = Vector2.ZERO
var speed = 600
var velocity = Vector2.ZERO

func _ready():
    randomize()
    new_position = Vector2(rand_range(50, 950), rand_range(50, 550))

func _physics_process(delta):
    velocity = position.direction_to(new_position) * speed
    print(velocity)
    if position.distance_to(new_position) > 2:
        velocity = move_and_slide(velocity)

func _on_MoveTimer_timeout():
    randomize()
    new_position = Vector2(rand_range(50, 950), rand_range(50, 550))

Example output of print:

(-473.734711, 368.205627)
(-473.73468, 368.205627)
(-473.734711, 368.205719)
(-473.73468, 368.205688)
(-473.73468, 368.205719)
(-473.73468, 368.205719)
(-473.734589, 368.205902)
(473.733551, -368.207153)
(-473.734589, 368.205902)
(473.733551, -368.207153)
(-473.734589, 368.205902)
(473.733551, -368.207153)
...
(-546.8479, 246.895569)
(-546.847839, 246.895569)
(-546.8479, 246.895569)
(-546.847839, 246.895569)
(-546.8479, 246.895554)
(-546.8479, 246.895554)
(-546.847839, 246.895538)
(-546.8479, 246.895538)
(-546.8479, 246.895523)
(-546.8479, 246.895493)

Is this some bug in Godot?
This could be unnoticed if sprite of this object had odd number of pixels in every direction, however I have even count of pixels and some rotating animation and final effect is like two interlaced object flipping between places.

:bust_in_silhouette: Reply From: njamster

No, that is not a bug in a Godot, but a “bug” in your code. You’re calling move_and_slide if the distance from the current position to the new position is at least 2. However, if you’re running physics_processwith 60 FPS, you’ll move by a distance of 10 (i.e. speed/fps) each frame. Now depending on the exact start- and target-position, you might end up overshooting the target (in which case the sign of the value direction_to returns will flip) by enough (i.e. more than 2) to enter an endless loop of overshooting the target. What you want to do is:

if position.distance_to(new_position) >= velocity.length() * delta:
    velocity = move_and_slide(velocity)
else:
    velocity = move_and_slide(position.direction_to(new_position) * position.distance_to(new_position) * 1/delta)

Thanks. I knew I forgot something. The value like 2 or put this in physics_process instead of process gave me less flipping but I totally forgot about fact, that this will be executed 60 times per second and the delta has its purpose.

kamilko | 2020-04-26 12:08