How to make an turret rotate towards the player at a constant speed?

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

In my game I have a turret, which I want it to be constantly aiming to the player, and the rotation needs to be at a constant speed (slow)

I tried this:

var rotation_speed = 0.1
    
func _process(delta):
    var player_dir = get_parent().get_node("Player").global_position - global_position
    
    rotation = lerp_angle(rotation,player_dir.angle(),rotation_speed)

But it makes the turret to rotate faster when it’s trying to aim to the player and to slow down as it gets closer

Is there any way to fix that?

Have you tried using look_at()? Is your game 2D or 3D?

ianzzap | 2021-01-23 07:11

Yeah, I tried that, but I need a delayed rotation

Edit: is a 2D game

yyyyk | 2021-01-23 20:26

:bust_in_silhouette: Reply From: Lopy

To go toward a value at a constant speed, you generally divide the difference by its length, and then multiply it by the speed:
var difference := player_dir.angle() - rotation
if difference != 0:
. rotation = rotation + (difference / abs(difference) * speed)

Dividing by the length is how you normalize a vector, but this is an angle, so dividing by the length just gives you one.

exuin | 2021-01-23 15:51

Well, I added my solution but after some tests I saw that yours also works, even with less lines of code
The only problem I had with your solution is that I had to speed * delta
Thank you!!!

yyyyk | 2021-01-24 02:54

:bust_in_silhouette: Reply From: yyyyk

Ok I figured it out

I had to use the difference of the angles (as Lopy said) and the dot product

I’ll put the code here in case anyone needs it:

var rotation_speed = deg2rad(45.0) # Since all values are in radians, this needs to be in radians too

func getDot(vector1: Vector2, _vector2:Vector2) -> float:
	var n1 = vector1.normalized()
	var n2 = _vector2.normalized()
	
	var vDot = n1.dot(n2.tangent()) # Why the tangent? Because the dot product is 0.0 when the angle between 2 vectors is 90º, and I need it to give 0.0 when the angle is 0º

	return vDot

func _process(delta):
	var player_dir = get_parent().get_node("Player").global_position - global_position
	var angle = polar2cartesian(1.0,rotation) # This var is to obtain a vector with the turret angle so I can obtain a dot product 
	var difference : float = player_dir.angle() - angle.angle()
	dot = getDot(angle, player_dir)
	
	if difference != 0:
		if abs(difference) > 0.1:
			$icon.rotation += sign(dot) * rotation_speed * delta # If the dot product is < 0 that would make the turret to rotate counter-clockwise and the other way around

Maybe this can be optimized, but it works
Thanks for the help!