2D Top Down look_at and lerp_angle smoothing

Godot Version

4.4

Question

Hello there,

I’ve been trying to research this for a couple days, and have seen several posts related to this topic but for some reason I can’t figure this out attempting some of the solutions out there.

I have a ‘top-down’ 2D space game, that moves my little space ship to the location of my mouse click. My script is very simple, and my ship turns to look at the direction it’s going but I don’t like the look and feel of the snappiness of the turn on click. I’d like to smooth over the look_at when turning to a new mouse click position. Reading forum posts it seems like one of the best ways to achieve this is with lerp_angle. I’ve tried it many different ways and none are doing the trick for me. I feel like I have to be close with the following script

extends CharacterBody2D

@export var speed = 400

var target
const TURN_SPEED = 0.25

func _ready():
	target = global_position

func _input(event):
	if event is InputEventMouseButton and event.is_pressed():
		if event.button_index == MOUSE_BUTTON_RIGHT:
			target = get_global_mouse_position()

func _physics_process(delta):
	
	velocity = global_position.direction_to(target) * speed * delta
	
	look_at(target)
	#rotation = lerp(rotation, atan2(-target.x, -target.y), TURN_SPEED)
	print(rotation)

	rotation = lerp_angle(rotation, get_angle_to(target), TURN_SPEED)
	move_and_slide()

Linearly interpolates between two angles (in radians) by a weight value between 0.0 and 1.0.

Similar to lerp(), but interpolates correctly when the angles wrap around @GDScript.TAU. To perform eased interpolation with lerp_angle(), combine it with ease() or smoothstep().

So I’m thinking the first argument being the current rotation radian, second parameter the target radian, and I tried to do a slow turn speed to see any effects. As it stands, my ship still snaps towards the direction of the mouse click but doesn’t snap exactly to where I clicked. It seems like the more I click to the ‘left’ (x-axis) of the ship, the less my ship wants to ‘look at’ the the spot I clicked.

I almost feel like the first parameter of current rotation is what is throwing this off, but I don’t know exactly… Any help would be appreciated.

Since this function returns void its applying the full look at rotation instantly. This is causing your snap.

You need to get the look at target transform with the Node’s transform without modifying the Node.

var lookat_xform : Transform2D = global_transform.looking_at(target)

Then with the lookat_xform you can use one of the basis vectors you consider front/forward to do a spherical linear interpolation.

var lookat_vec : Vector2 = global_transform.x.slerp(lookat_xform.x, TURN_SPEED) #use whatever axis needed
look_at(lookat_vec) # do the incremental look at with the slerp'd vector

Vector 2 also has cubic interpolation if that’s something you prefer.

Additionally, you do not want to use lerp_angle() if you are changing the current rotation and still using it at the first parameter. This makes it no longer linear.

For example, a starting value of 0.0 interpolated to 1.0 halfway (a weight of 0.5) would be 0.5. But on the next call to lerp_angle(), with 0.5 as the starting value and 1.0 as end and still a weight of 0.5, the value is now 0.75 instead of 1.0 like it should be.

Use the function rotate_toward() if you are updating the starting angle. (I think pennyloafer’s code will work in the intended way however because it’s not altering the starting transform)

Thank you folks!~