How do i make things rotate smoothly?

Godot Version

4.2.1

Question

I want to make my enemy smoothly turn towards my player after its done dashing instead of just snapping.

extends CharacterBody2D

@onready var player = get_node("/root/Main/Player")
@onready var move_timer = %Move_timer
@onready var delay_timer = %Delay_timer

var speed = 1500
var dashing = false
var direction
var rotation_speed = 0.1

func _physics_process(delta):
	if dashing == false:
		direction = (player.global_position - global_position)
	
	rotation_degrees = rad_to_deg(direction.angle())
	velocity = (direction.normalized()) * speed
	
	rotation_degrees = lerp_angle(rotation_degrees, rotation_degrees, rotation_speed * delta)
	move_and_slide()

func _on_move_timer_timeout():
	speed = 2500
	dashing = true
	delay_timer.start()

func _on_delay_timer_timeout():
	speed = 1500
	dashing = false

i tried with the line
rotation_degrees = lerp_angle(rotation_degrees, rotation_degrees, rotation_speed * delta)
but it still just snaps towards the player so i clearly dont understand how it works so i would appreaciate telling me how to do this.

From Godot Docs:

float lerp_angle ( float from, float to, float weight )
Linearly interpolates between two angles (in radians) by a normalized value.

Error #1

In the code snippet you provided, you are interpolating from/to the same value (rotation_degrees). Therefore, the result of the lerp_angle() will always be rotation_degrees.

Error #2

Prior to computing and setting your rotation via lerp_angle(), you have already “snapped” the rotation to direction.angle(). Any smoothing that may have been performed via your lerp_angle()-call is nullified because your rotation has already reached it’s target value.

Note:
In your code you are still using lerp_angle() incorrectly. The above is a description of your code if it were correct everywhere else.

Solution

// The current rotation (in radians)
var currentRotation = rotation
// The rotation that should be interpolated towards (in radians)
var targetRotation = direction.angle()
// Set the rotation to an interpolated value.
rotation = lerp_angle(currentRotation, targetRotation, rotation_speed * delta)

I tried doing everything you told me, but it still does the same thing as before. Here is the current code i have after trying to do everything you’ve told me, if you can help me out more.

extends CharacterBody2D

@onready var player = get_node("/root/Main/Player")
@onready var move_timer = %Move_timer
@onready var delay_timer = %Delay_timer

var speed = 1500
var dashing = false
var direction
var rotation_speed = 1.0

func _physics_process(delta):
	if dashing == false:
		direction = (player.global_position - global_position)
		rotation_degrees = rad_to_deg(direction.angle())
	
	var current_rotation = rotation_degrees
	var targetRotation = direction.angle()
	
	rotation_degrees = lerp_angle(current_rotation, targetRotation, rotation_speed * delta)
	velocity = (direction.normalized()) * speed
	
	move_and_slide()

func _on_move_timer_timeout():
	speed = 2500
	dashing = true
	delay_timer.start()

func _on_delay_timer_timeout():
	speed = 1500
	dashing = false


You didn’t quite do as I described.
I get it though. Sometimes similar terms can be overlooked.

Just replace your full _physics_process() with the following code:
Pay close attention to the fact that I’m using rotation instead of rotation_degrees:

func _physics_process(delta):
    if dashing == true:
        direction = (player.global_position - global_position)
        rotation = direction.angle()

    var current_rotation = rotation
    var target_rotation = direction.angle()
    
    rotation = lerp_angle(current_rotation, target_rotation, rotation_speed * delta)
    velocity = (direction.normalized()) * speed
    
    move_and_slide()

Note:
When working with rotations, always make sure that you know what unit you’re working with (radians or degrees). Confusing the two will cause issues such as this.

lerp_angle() operates in radians so the parameters for the function should be in radians as well. That is why I’m using rotation which is the object’s rotation in radians (as opposed to rotation_degrees which is in degrees).

If it still doesn’t work, no worries. I’m liable to doing some errors as well as I’ve never used GDScript.

image

i get this error on the line

var target_rotation = direction.angle()

Oh, right.

Because you moved your direction-variable into an if-statement, it is no longer in the same scope as the rest of the function (i.e. it only exists within the if-statement). You should define direction before the if-statement.

Example

// ...
    var direction = Vector2() // This line was added.
    if dashing == true:
        direction = (player.global_position - global_position)
        rotation = direction.angle()
// ...

EDIT: Wait, but you already defined direction in the top of the script, right?
You shouldn’t be getting this error.

I thought so too but i added the Vector2() and i dont get an error anymore but it still doesn’t rotate smoothly and still snaps

Okay. Obviously I don’t know what the dashing is for.

Try removing this:

    if dashing == true:

and this:

        rotation = direction.angle()

Its basicly a dash, that i don’t want while being performed, the boss to look at the player so if he isn’t dashing he just looks at the last known player position which is right before dashing. Removing those lines just make the boss not rotate properly. And if i remove only the if, then the boss just constantly looks at the player even when dashing. And thats basicly my idea of the smooth rotating. I want my boss to rotate towards the player after done dashing.

Right.

Sorry about my dumb suggestion. Sometimes looking at code in a language I’m not familiar with confuses me.

1 Like

Yeah fair enough. Thanks for trying to help at least.

If you don’t want it to rotate at all wihe dashing then you just have to put everything rotation related under if dashing == false, like:

if dashing == false:
	direction = (player.global_position - global_position)
	var current_rotation = rotation_degrees
	var targetRotation = rad_to_deg(direction.angle())
	rotation_degrees = lerp_angle(current_rotation, targetRotation, rotation_speed * delta)

edit: whoops, replied to the wrong post…

No worries

Seems right, although you’re also using degrees instead of radians which doesn’t work for lerp_angle().

The not rotating during dash works fine, but my problem is getting the boss to rotate towards the character instead of snapping to it.

Okay! I’m gonna go into Godot now and fix this script!
I can’t take it!

1 Like

Oh, I don’t use degrees so don’t know about that part…

Yeah i’m not quite used to difference between radians and degrees since im very new to godot.

Well, pretty sure it should work like this

if dashing == false:
	direction = (player.global_position - global_position)
	var targetRotation = direction.angle()
	rotation = lerp_angle(rotation, targetRotation, rotation_speed * delta)

This works actually thank you.

Finally! Good job, guys!

I was about to go crazy!