Rotation wrap-around issue

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

Hi there,
I’m trying to make a player rotate to face in the direction of a virtual joystick. I don’t want to use look at(), because I want to be able to adjust the turning speed of the player, not have it instantly look in the direction of the joystick (unless this is possible with look at()).

Because I’m checking if rotation is larger or smaller than target dir, whenever target dir wraps around (goes from PI to -PI or the other way around), the player starts turning in the wrong direction.
Is there some obvious way around this, or built-in function I’m not seeing?
I appreciate any insight into this.

Here is the relevant code:

##Joystick movement
#…
var target_dir
if !is_reversing:
target_dir = Joystick.output.angle()
else:
target_dir = atan2(-Joystick.output.y, -Joystick.output.x) #opposite direction of Joystick in radians

    #a temporary, imperfect workaround. This makes it jumpy, and also does not work if the angle changes too suddenly.
	if rotation > 2.7 && target_dir < -2.7 :
		rotation = -PI
	if rotation < -2.7 && target_dir > 2.7 :
		rotation = PI
	
        #Set rotation direction
	rot_dir = 0
	if rotation > target_dir:
		rot_dir -= 1
	else:
		rot_dir += 1
#finally, apply rotation based on direction set.
rotation += rotation_speed * rot_dir * delta
:bust_in_silhouette: Reply From: Nermit

##Update:
Solved my problem thanks to Dlean Jeans’ answer here:
https://forum.godotengine.org/48342/lerp-rotation-with-above-180-degrees-difference?show=48342#q48342

#Replaced

 #Set rotation direction
rot_dir = 0
if rotation > target_dir:
    rot_dir -= 1
else:
    rot_dir += 1

#With

rot_dir = round(_short_angle_dist(rotation, target_dir))

Adding this function to the bottom of my script:

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

Using round() will make rot_dir round to 2 or -2 if the angle is extreme, but I have decided to leave that in for now, as it just makes my movement feel a little snappier. You could always check if rot_dir > 1 or < -1.