Slowly interpolate look_at() function for my enemy

Godot Version

v 4.3

Question

Similar question has already been solved here: Using a lerp() to interpolate a look_at() function? - #5 by gertkeno

The solution is frame-dependent, and I do not want a frame-dependent solution!

I have an enemy with an Area3D. When the player enters the Area3D, I want my enemy to slowly look towards the player. For now, my code looks like this!


func _on_fov_body_entered(body: Node3D) -> void:
	if body.is_in_group("player"):
		print("Player Alert!")
		look_at(Vector3(body.global_position.x, self.global_position.y, body.global_position.z))

Here in tween_method documentation they are using the look_at method to slowly pan from point to point.

However tweening the euler angles like this is a bad practice and can result in some very strange rotations. That’s why we have quaternions. The link you posted is actually a really good advice, you just wrap it in a tween to make it frame independent like you wanted.

The Basis.slerp documentation actually has the exact example you are looking for!

I’d just add to that, that you can use Callable.bind just so that the arguments don’t have to be in the global scope. Unless you want that.

So instead of the examples in the Basis docs you could do:

func _ready():
	var start_basis = Basis.IDENTITY
	var target_basis = Basis.IDENTITY.rotated(Vector3.UP, TAU / 2)

	create_tween().tween_method(interpolate.bind(start_basis, target_basis), 0.0, 1.0, 5.0)

func interpolate(weight: float, from: Basis, to: Basis) -> void:
	basis = from.slerp(to, weight)

The tween will supply the weight argument and the rest you provided with the bind(...). Docs:

When called, the bound arguments are passed after the arguments supplied by call.

Hope it helps

I use this approach, it works as expected:

func look_at_position(target_position: Vector3, delta: float, turn_speed: float) -> void:
	var target_vec := target_position - global_position

	if not target_vec.length():
		return

	var target_rotation := lerp_angle(
		global_rotation.y,
		atan2(target_vec.x, target_vec.z),
		turn_speed * delta
	)
	global_rotation.y = target_rotation