Hi ! We are building an endless runner set in a mazy city where the player can take many different routes. The path-following script which controls player direction is based off the get_closest_offset()
function and seems to be running well. Our concern is the path-switching method to use to switch smoothly between different paths : as newcomers to the Godot engine AND programming, we are still trying different solutions without knowing which one would be the most suitable for a more fully-featured environment.
Using Godot 3.5.2
Current solutions :
1) A path-finder area in the player scene gets the path child of a path-switching area it comes across (code below), it's really prone to bug and need to be carefuly set in the engine
2) A larger path-switching area with two paths as exported variables compare the player distance from each of the paths closest offsets and interpolates player direction until it leaves the area (not written yet)
(possible setup, with the same paths as in the previous picture)
3) A navigation map ? We haven't used it before but it might be a suitable solution
So the question is, are there better solutions to achieve this, maybe without having to manually set path-switching areas, or even curved paths, again maybe with NavigationMaps ?
Here are the parts of the script for the first solution:
(The path script is based on the path-based-movement-demo (https://github.com/Pyxus/path-based-movement-demo) written by Pyxus.)
Player script
var h_input: float
var direction := Vector3.ZERO
var velocity := Vector3.ZERO
# Get the starting area child path
onready var current_path = get_new_path(get_node(starting_area))
func _physics_process(delta):
# Interpolates between the old and new path
var previous_direction = path_direction
if path_inertia < 20.0:
path_inertia += 0.1
path_direction = get_path_direction(current_path, previous_direction, path_inertia)
var look_at_target = translation + path_direction.rotated(Vector3.UP, PI * 2)
if translation != look_at_target:
look_at(look_at_target, Vector3.UP)
direction = path_direction
# Get input for lateral movement
h_input = lerp(h_input,
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left"), acceleration * delta
)
direction += h_input * transform.basis.x
velocity = lerp(velocity, direction.normalized() * speed, acceleration * delta)
velocity = move_and_slide_with_snap(velocity, snap, Vector3.UP)
#An area in the player scene detects path_switcher areas and get its child path as its current path
func _on_PathFinder_area_entered(area: Area) -> void:
path_inertia = 1.0
if get_new_path(area) != null:
current_path = get_new_path(area)
Base class the player class extends
class_name KinematicPathTraveller
extends KinematicBody
const NEXT_POINT_OFFSET: float = 0.01
const INERTIA: float = 1.0
# Path-following script
func get_path_direction(path, previous_direction, inertia) -> Vector3:
var closest_offset = path.curve.get_closest_offset(path.to_local(transform.origin))
var point_a = path.curve.interpolate_baked(closest_offset)
var point_b = path.curve.interpolate_baked(closest_offset + NEXT_POINT_OFFSET)
var path_direction = lerp(previous_direction, point_a.direction_to(point_b), inertia * get_physics_process_delta_time())
return path_direction
func get_new_path(area) -> Path:
var new_path: Path
for child in area.get_children():
if child is Path:
new_path = child
return new_path