How to lerp between two angles?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Zylann
:warning: Old Version Published before Godot 3 was released.

Is there a GDScript function to lerp between two angles?
For example, make it so 270° => 20° doesn’t go backwards.

:bust_in_silhouette: Reply From: genete

One possible solution is to convert angle to float and lerp with the float, then convert to back to angle and use it.

What do you mean by angle? My angle is already a float variable.

Zylann | 2016-06-30 23:39

:bust_in_silhouette: Reply From: vinod

Take normalized directional vectors for angles.
A vector 1,0 will be 0 deg, vector 0,1 will be 90deg.

Then lerp between these vectors and find the angle of the resulting vector.

There are easier solutions for this if you manually calculate the angle instead of lerping. Or if you want to lerp the rotation of something, there is quaternion slerp in gdscript.

I actually compute the angle myself, and it’s 2D so I don’t use quaternions. Converting back and forth in vectors could work but I’m sure there is a faster way.

Zylann | 2016-07-01 12:45

:bust_in_silhouette: Reply From: Zylann

I found a solution:

static func lerp_angle(a, b, t):
    if abs(a-b) >= PI:
	    if a > b:
		    a = normalize_angle(a) - 2.0 * PI
	    else:
		    b = normalize_angle(b) - 2.0 * PI
    return lerp(a, b, t)


static func normalize_angle(x):
    return fposmod(x + PI, 2.0*PI) - PI

You can add it to your helpers :stuck_out_tongue:

An example project:
http://zylannprods.fr/dl/godot/LerpAngle.zip

Thank you! I’ve been trying to figure this out for a while.

hnuqweasd | 2018-02-28 22:20

:bust_in_silhouette: Reply From: Dlean Jeans

EDIT: Godot now has a lerp_angle function.


Zylann’s solution doesn’t seem to work when there’s an offset before the lerping:

func _process(delta):
	var to_mouse = $Sprite.global_position - get_global_mouse_position()
	var offset = -PI / 2
	var rotation_to_mouse = to_mouse.angle() + offset
	$Sprite.rotation = lerp_angle($Sprite.rotation, rotation_to_mouse, 0.25)

So this is an alternative which works in that case
adapted from https://gist.github.com/shaunlebron/8832585:

func lerp_angle(from, to, weight):
	return from + short_angle_dist(from, to) * weight

func short_angle_dist(from, to):
	var max_angle = PI * 2
	var difference = fmod(to - from, max_angle)
	return fmod(2 * difference, max_angle) - difference

thanks, man. it works!

vfpe | 2021-01-01 18:54

:bust_in_silhouette: Reply From: Steely Wing

You can use wrapf, wrap the difference in -PI to PI

func lerp_angle(from, to, weight):
    var diff = wrapf(to - from, -PI, PI)
    var i = lerp(from, from + diff, weight)
    # Keep the result in 0..2*PI
    i = wrapf(i, 0, 2*PI)
    return i


func _ready():
    for i in range(0, 10):
        print(rad2deg(lerp_angle(deg2rad(270), deg2rad(20), i/10.0)))