Using move_and_slide with nav path generated by get_simple_path

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

Hi everyone. I recently started using Godot to make my first game which is a 3D role playing game using mouse click to move.

I used the Navmesh template provided and imported my own models and attempted to replace the set_transform() function to use move_and_slide on my KinematicBody Character Node.

I have the character following the path but the movement speed is uneven. Between some points, the character moves slowly, between other points he zooms past really quickly. I think this may be because there are more points between some sections and fewer in other sections.

I’m looking to see if there is a better way to accomplish this. In fact I almost certain there is and could use some guidance. For example, I am checking if the character is close to his next point before removing the point from the array and getting the next point.

Here is a snip of my character nodes script that is handling the movement:

func _set_path(p):
    path = p
    set_physics_process(true)

func _physics_process(delta):
    var direction = Vector3(0,0,0)

if(path.size() > 0):
	var next_position = path[path.size()-1]
	direction = next_position - translation;
	direction.normalized()
	look_at(next_position + Vector3(direction.x,0,direction.z),Vector3(0,1,0))
	move_and_slide(direction * SPEED, Vector3(0,1,0),0.05,4,1)
	if(abs(translation.x - next_position.x) <= 0.05 and abs(translation.z - next_position.z) <= 0.05):
		path.remove(path.size() - 1)
	var anim = get_node("Character/AnimationPlayer")
	if(anim.is_playing() and anim.assigned_animation != "Anim_Run-loop"):
		anim.stop()
		anim.play("Anim_Run-loop")
else:
	set_physics_process(false)
:bust_in_silhouette: Reply From: Baolnar

I figured out what was missing.

I needed to use the linear_interpolation function to get a point between the characters current position and the next_position for a smoother movement.

I do this by having a global variable called “next_move_pos = Vector3(0,0,0)”

func _physics_process(delta):
var direction = Vector3(0,0,0)

if(path.size() > 0):
	var next_position = path[path.size()-1]
	if(next_move_pos == Vector3(0,0,0)):
		next_move_pos = next_position
	var distance = translation.distance_to(next_position)
	if(distance != 0):
		next_position = translation.linear_interpolate(next_position, (SPEED*delta)/distance)
	direction = next_position - translation;
	direction.normalized()
	#direction.y = direction.y * -9.8
	look_at(next_move_pos + Vector3(direction.x,0,direction.z),Vector3(0,1,0))
	move_and_slide(direction * SPEED, Vector3(0,1,0))
	if(abs(translation.x - next_move_pos.x) <= 0.05 and abs(translation.z - next_move_pos.z) <= 0.05):
		path.remove(path.size() - 1)
		next_move_pos = Vector3(0,0,0)
	var anim = get_node("Character/AnimationPlayer")
	if(anim.is_playing() and anim.assigned_animation != "Anim_Run-loop"):
		anim.stop()
		anim.play("Anim_Run-loop")
else:
	set_physics_process(false)

Another change is that the condition that determines removing the current point in the path and getting the next one, looks at the next_move_pos global variable rather than the linear_interpolated next position which changes often.

I still think there has to be a better way of determining that we have reached a point in a path and should now start moving to the next point than comparing the x and z position with the characters position with some leeway (0.05 in my case).

I’m using it with a little tiny change:

var direction = Vector3(0,0,0)
var initialTranslation = translation   # Gatting the initial translation before moving
if(path.size() > 0):
    ...
    look_at(Vector3(next_move_pos.x, initialTranslation.y, next_move_pos.z) + Vector3(direction.x, 0, direction.z),Vector3(0, 1, 0))  # Using the y axis of the "initialTranslation"
    ...

Victoralm | 2019-05-09 20:37