How can I use move_and_slide to move a CharacterBody2D along a Path2D?

Godot Version

4.4 beta 1

Question

I want to implement an enemy that patrols along a predefined path but my code isn’t working. I want to use move_and_slide so I think I can’t use the PathFollow2D node. What am I missing here?

class_name Enemy
extends CharacterBody2D

@export var path: Path2D

var speed := 10
var min_distance := 2

var current_target_index := 0
var baked_points: PackedVector2Array

func _ready() -> void:
	baked_points = path.curve.get_baked_points()


func _physics_process(delta: float) -> void:
	if global_position.distance_to(baked_points[current_target_index]) < min_distance:
		current_target_index = wrapi(current_target_index + 1, 0, baked_points.size())
	
	velocity = global_position.direction_to(baked_points[current_target_index]) * speed
	
	move_and_slide()

In general, it seems like your approach should work. Try printing the distance to the next point to understand better what is wrong.


func _physics_process(delta: float) -> void:
	print(global_position.distance_to(baked_points[current_target_index])
	if global_position.distance_to(baked_points[current_target_index]) < min_distance:
		current_target_index = wrapi(current_target_index + 1, 0, baked_points.size())
	
	velocity = global_position.direction_to(baked_points[current_target_index]) * speed
	
	move_and_slide()

I’ve found a solution. I had to extract the points of the curve using the get function.

func _ready() -> void:
	for i in patrol_path.curve.point_count:
		var patrol_point: Vector2 = patrol_path.curve.get("point_" + str(i) + "/position")
		patrol_points.append(patrol_point)

func _physics_process(delta: float) -> void:
	if reached_patrol_point:
		return
	
	if absf(actor.global_position.x - current_patrol_point.x) < MIN_DISTANCE_TO_PATROL_POINT:
		reached_patrol_point = true
		move_to_next_patrol_point()

	actor.velocity.x = (-1 if actor.global_position.direction_to(current_patrol_point).x < 0 else 1) * PlayerMoveState.max_speed * speed_factor
	
	if not actor.is_on_floor():
		actor.velocity += actor.get_gravity() * delta
	
	actor.move_and_slide()

func move_to_next_patrol_point() -> void:
	patrol_index = wrapi(patrol_index + 1, 0, patrol_points.size())
	current_patrol_point = patrol_points[patrol_index]
	actor.visuals.scale.x = -1 if actor.global_position.direction_to(current_patrol_point).x < 0 else 1
	reached_patrol_point = false