Having trouble with my tank turret rotation system :(

Godot Version 4.2

Question

gif : Godot-Question hosted at ImgBB — ImgBB
problem with turret rotation in my topdown tank game

hi friends
context :
im trying to make a topdown tank game in which every tank has 4 components , tracks , body , turret and the gun
im trying to give the turret a turn_rate which indicates the speed of turret turning to the target position(in player case global_mouse_position) , i use move_toward() instead of lerp() cause i want constant rotation speed

so the final effect i wanna achive is thet when player aimes at somthing there is a delay based on turn_rate for turret to look at that target

here is the problem :
some times when the mouse cursor is behind turret it suddenly shifts to it without rotataion !

note(turret difault direction/angle is upward thats why you see +PI/2 every where)

and the other issue as you can see is that some times turret rotattes oposite of the shortest direction (for example clock wise while its petter to do it counter clock wise)

here is the code (btw turret_turn_rate = 0.2):

func _process(delta):
    rotate_turret(delta)

func rotate_turret(delta):
	var target = get_global_mouse_position()
	var is_reached = func() : return abs(turret.global_rotation - turret.global_position.direction_to(target).angle() + PI/2 )< turret_turn_rate*delta
	if  is_reached.call():
		turret.global_rotation = turret.global_position.direction_to(target).angle() + PI/2
		return
	turret.global_rotation = move_toward(turret.rotation,turret.global_position.direction_to(target).angle() + PI/2,turret_turn_rate*delta)
	
    

There are many ways to solve this problem, but the important thing is to make sure the distance to the target angle doesn’t exceed 180 degrees.

Here’s my untested example.

# Get target angle
var target_angle = turret.global_position.angle_to_point(get_global_mouse_position()) + PI/2.0

# Change the prespective so that the full turn around the current angle is between 0 and 360 degrees.
target_angle = target_angle - turret.global_rotation + PI

# Limit the range of target angle values
target_angle = fposmod(target_angle, TAU)

# Now the target angle is a value between 0 and 360 degrees.
# Change the prespective back to what is was before.
target_angle = target_angle + turret.global_rotation - PI

# Now the distance to the target angle is limited to half a turn so we can just move toward it.
turret.global_rotation = move_toward(turret.global_rotation, target_angle, turret_turn_rate * delta)
1 Like

In Godot 4.2 there’s a new rotate_toward() function that should avoid spinning the wrong way.

1 Like

hi grulps !
i guess one of the reasons i don`t quiet get your solution i the fact idk anything about ho rotation works in Godot but the fact of it using radians to store rotation

for example i made a simple scene with look_at(get_mouse_global_position()) and printed its rotation and weirdly it was showing +values till 3 quarters (clockwise , from x axis btw) and the last quarter (or first if you go counter clock from x axis) was negative , why the hell is that :\ , i mean does`nt it make sens just go with positive value or divide it by 2 (half negative , half positive), instead of this 3/4 pos 1/4 neg ?

oh ! and btw can you give me your assumption of why the hell some times when the mouse cursor is behind turret it suddenly shifts to it without rotating (understanding the problem may help me understand the solution better)
*apologize beforehand if I’m just dumb or missing some thing trivial

In my solution it doesn’t matter how much the angles go to positive or negative sides. Anyway I recommend using rotate_toward() like Monday suggested.

func rotate_turret(delta):
    var target_angle = turret.global_position.angle_to_point(get_global_mouse_position()) + PI/2.0
    turret.global_rotation = rotate_toward(turret.global_rotation, target_angle, turret_turn_rate * delta)

You don’t even need to check, if the target has been reached, when using rotate_toward(), unless it’s needed for some game mechanic.

As for the sudden changes in rotation, it’s probably because you forgot to add parenthesis to this part of is_reached, so the order of operations was wrong.

turret.global_rotation - (turret.global_position.direction_to(target).angle() + PI/2)
1 Like

thanks !