How do I work with angles using vector3 basis?

Godot Version

v4.3.stable.mono.official [77dcf97d8]

Question

My goal is that I want my character to look at the enemy, but smoothly. Because of that I do not want to use any look_at function and such, I want to make my character turn left if the angle to the target is lower than 0 degrees, and right if the angle is higher than 0.

I have to be honest, this is pretty hard for me to ask about, because I am not sure if I am approaching this wrong.

I have a function here that is getting the angle to an object:

func get_angle_to() -> float:
	var target_2d_pos: Vector2 = Vector2(target.global_position.x, target.global_position.y)
	
	var forward = rotation_point.global_transform.basis.z
	
	var current_2d_pos: Vector2 = Vector2(forward.x, forward.z)
	
	#print(rotation_point.global_transform.basis.z)
	
	var direction_to = current_2d_pos.direction_to(target_2d_pos)
	
	var angle_to := current_2d_pos.angle_to(target_2d_pos)
	
	label_3d.text = "angle: %.2f" % rad_to_deg(angle_to)
	
	return angle_to

Already here I have a problem, which is that I actually want to calculate the angle from the forward direction of this character, but I’m pretty sure that it uses its global position, becuase basis, is not working in vector 2:

The rotation function code looks like this:

func rotate_to_point(angle: float, delta) -> void:
	var rotation_speed: float = 100
	
	var turn_amount = deg_to_rad(rotation_speed * delta)
	
	var turn_dir = 1
	
	if rad_to_deg(angle) < 0:
		turn_dir = 1
	else:
		turn_dir = -1
	
	rotation_point.rotate_object_local(Vector3.UP, turn_dir * turn_amount)

And I’m pretty sure that this is fine, as long my get_angle_to() function gets fixed.

This is how the scene tree looks:
image

I would use the look_at function, but use a lerp to interpolate between the desired state and current state.

If you want the MainCharacter to look at a target without looking up and down, you might be able to use this get_angle_to() function:

func get_angle_to(target:Vector3) -> float:
	var target_2d_position := Vector2(target.x, target.z) #Get the location of the target as a Vector2 from a top-down perspective

	var self_2d_position := Vector2(position.x, position.z) #Same thing as above. They both need to have the same origin.
	#If you target is in global coords, use global_position.x and .z rather than just position

	var relative_target_direction: Vector2 = target_2d_position - self_2d_position #Vector from self to target on a top-down plane

	var forward_3d_direction: Vector3 = global_transform.basis.z #Forward direction of self in 3d. I might have this wrong, replace this with the forward direction the the player.

	var forward_2d_direction := Vector2(forward_3d_direction.x, forward_3d_direction.z)

	var angle_to_target: Vector2 = forward_2d_direction.angle_to(relative_target_direction) #Get angle to target

	return angle_to_target

	#*THIS IS UNTESTED AND MAY NOT WORK

Then use lerp to smooth it out. If you want to use look_at, use it, set the result.y to zero, normalize it if you want and use that angle.

How would I use lerp for look_at function? Doesn’t lerp need a current and a desired value? What would the current value be in this case?

There are a few levels of look_at.

Node3D.look_at
Transform3D.looking_at
Basis.looking_at

Since Node3D doesn’t allow us to control the out come. We need to use the transform3D.looking_at which returns a transform3d object.

# inside a process function
var target_xform = self.global_transform.looking_at(target_position)
self.global_transform = self.global_transform.interpolated_with(target_xform, 0.1)