Similar to Unity's Mathf.LerpAngle.
This is my implementation in GDScript:
static func lerp_angle(from, to, weight):
return from + _short_angle_dist(from, to) * weight
static 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
This would be really useful to have indeed. Here's some simplified/optimized version:
static func lerp_angle(from, to, weight):
var difference = fmod(to - from, TAU)
var short_angle_dist = fmod(2 * difference, TAU) - difference
return from + short_angle_dist * weight
@Xrayez Are you sure that its correct algorithm ? In Unity
Mathf.LerpAngle(0.0, 90.0, 1.0) // == 90
while in your proposal
lerp_angle(0.0, 90.0, 1.0) // == 116 (after converting to angles)
It's the same with my implementation. The function expects angles in radians not in degrees.
Ninja'd by @DleanJeans so writing an example now. 馃檪
var angle_rad = deg2rad(90.0) # PI / 2
var interpolated = lerp_angle(0.0, angle_rad, 1.0)
print(rad2deg(interpolated)) # 90
And why you change it to radians when in Unity its in degrees ?
It makes sense to me to work in radians because many (Godot) math functions expect angles to be in radians already:
Vector2.rotated(radians)
var radians = Vector2.angle_to_point(Vector2)
var radians = sin(radians)
Those functions that expect degrees are often exposed directly in the engine's editor to make it easier to work with from user side and are converted to radians to be used internally anyway:
void Node2D::set_rotation_degrees(float p_degrees) {
set_rotation(Math::deg2rad(p_degrees));
}
So built-in GDScript functions should work in radians.
@Chaosus:
And why you change it to radians when in Unity its in degrees ?
I based it off a gist I found on GitHub. The link to Unity docs is just to simply explain it and to show that Unity has it then we should have it builted in too (though the real reason is that I saw on many occasions people asking about lerping angles in Godot).
@Xrayez for some reason it produce a negative values eg.
print(str(rad2deg(lerp_angle2(deg2rad(0), deg2rad(180), 1.0)))) # produces -180
while in Unity
print(Mathf.LerpAngle(0.0f, 180.0f, 1.0f)); // produces 180
If you fix or explain why this happens, I can create PR with your changes and add you as co-author
180 and -180 are the same thing. I think it doesn't make any difference.
Alright then..
Ideally even if the shortest angles are "equal" as in [0, 180], [0 -180] (minus would take counter-clockwise rotation), the rotation should take the route of the same sign, but lerping angle is mostly done for smoothing so this difference is not as important I think. Might be related to how fmod works internally there.
print(str(rad2deg(Utils.lerp_angle(deg2rad(0), deg2rad(-180), 1.0)))) also gives -180, so lets say it yields consistent results at least. The unit circle takes angles counter-clockwise according to my trigonometry knowledge.
@Chaosus I translated this initially to C++ but then stopped, feel free to add this if you haven't already:
static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) {
double diff = fmod(p_to - p_from, Math_TAU);
double shortest = fmod(2.0 * diff, Math_TAU) - diff;
return p_from + shortest * p_weight;
}
static _ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) {
float difference = fmod(p_to - p_from, Math_TAU);
float shortest = fmod(2.0f * difference, Math_TAU) - difference;
return p_from + shortest * p_weight;
}
wrapfcan be used instead fmod then, its supports negative wrapping.. (Just an idea - nothing more)
Most helpful comment
It makes sense to me to work in radians because many (Godot) math functions expect angles to be in radians already:
Those functions that expect degrees are often exposed directly in the engine's editor to make it easier to work with from user side and are converted to radians to be used internally anyway:
So built-in GDScript functions should work in radians.