How to move a player along a path, starting from the closest point between player & path

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

Path2D: How to get index of a closest baked point from described point

Hello everone,

I want to programatically move a player (Kinematic2D) along a path, not from start to end, but starting from a closest point (from player to curve) to end of the path. I refered this article, changed some code & built a test project as shown below: the player moves, but from start to end position.

My test scene:
┖╴root
┖╴Node2D
┠╴Path2D
┖╴KinematicBody2D
┠╴Sprite
┖╴CollisionShape2D

Code:

extends KinematicBody2D

var move_speed = 100
onready var patrol_path = get_node("../Path2D")
var patrol_points
var patrol_index = 0

func _ready():
	get_tree().root.print_tree_pretty()
	patrol_points = patrol_path.curve.get_baked_points()


func _physics_process(delta):
	if patrol_index > patrol_points.size() - 1:
		return
	
	var target = patrol_points[patrol_index]
	patrol_index += 1
	position = target

Actually what I want is that the player should move to a nearest point on the curve & then move until end. I do not know much about Path2D & related stuff, but what I understood is there are 2 arrays; one for the points that we describe in editor to make the shape of the curve (get_point_count()=55, in my scene) & another is for the points which are baked at 5px (as per the density) interval (get_baked_points().size()=329, in the same scene).

func get_curve_point_index_from_offset(curve, offset):
  var curve_point_length = curve.get_point_count()
  if curve_point_length < 2: return curve_point_length
  for i in range(1, curve.get_point_count()):
      var current_point_offset = curve.get_closest_offset(curve.get_point_position(i))
      if current_point_offset > offset: return i
  return curve_point_length

The above code is from From this post This code returns the index of nearest point in the curve, but that point is from the points which we describe in the editor. Using this code, I changed the code accordingly. I got the required result, but as the number of points in this array are small, the movement is finished very fast, which is not acceptable.

If I can get a point in baked/cached points array a smoother effect can be achieved. So my question is how can I get index of PoolVector2Array array from the index I got in described points.

Thanks!

:bust_in_silhouette: Reply From: Mak

I think you can use the interpolate_baked function like this :

#TOTALLY UNTESTED

var offset = 0
var target_point: Vector2
var on_line := false
var speed := 100
func _ready():
    offset = curve.get_closest_offset(global_position)
    target_point = curve.interpolate_baked(offset)

func _physics_process(delta):
    if on_line:
        global_position = curve.interpolate_baked(offset)
        offset += speed * delta
    elif global_position.distance_to(target_point) > DELTA:
        global_position += (target_point - global_position).normalized() * speed
    else:
        on_line = true

P.S. I also think it’s better to use function move_and_slide to move the character.

Thanks, in the meanwhile, I also watched this tutorial from which I got that using a PathFollow2D it is very easy to move the target along a path: follow.set_offset(follow.get_offset() + 350 * delta)

But again if the sprite is placed at different position other than start point of the curve, the movement misbehaves.

I will test your code suggestion tomorrow & let you know the results. Thanks again!

vbdev | 2021-05-01 20:07

Thanks… exactly what I needed.

Though you said that your code was not tested, i worked at the first time like a charm!!! Only I had to define DELTA which I easily figured out.

Really your answer was very helpful to me, thanks a lot again.

vbdev | 2021-05-02 08:53