Rotate object to face another object - 3D (similar to look_at)

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

I read in the docs that the ‘look_at’ function does not update each frame which I have noticed.

My question is: How does one go about rotating an object to face another. I have an enemy that I want to face my player. The enemy is a rigid body and the player is a kinematic body.

On the docs I’ve found this:

extends RigidBody

func look_follow(state, current_transform, target_position):
    var up_dir = Vector3(0, 1, 0)
    var cur_dir = current_transform.basis.xform(Vector3(0, 0, 1))
    var target_dir = (target_position - current_transform.origin).normalized()
    var rotation_angle = acos(cur_dir.x) - acos(target_dir.x)

    state.set_angular_velocity(up_dir * (rotation_angle / state.get_step()))

func _integrate_forces(state):
    var target_position = $my_target_spatial_node.get_global_transform().origin
    look_follow(state, get_global_transform(), target_position)

But I cannot get that to work. I’d be fine with just getting the angle somehow and using an if to rotate_y() until the angle is near zero.

I’m not great with vector math but I thought this would work:

var dir = (target_node.get_translation() - self.get_translation()).normalize()
var angle = dir.dot(Vector3(0,0,1))

if angle < -0.5:
     rotate_y(turn_speed)
elif angle > 0.5:
     rotate_y(-turn_speed)

The line of thought was I’d just rotate along the Y axis until I’m within a tolerance (the 0.5) which meant the enemy was facing the player. But that logic doesn’t work. So I must not be understanding the math.

:bust_in_silhouette: Reply From: MysteryGM

I read in the docs that the ‘look_at’ function does not update each frame

It updates when called, so if you call it once per frame it will in fact update each frame. Just tested it:

extends Sprite

var CountLooksPerSecond = 0
var OldRotation

var time = 1


func _process(delta):
	if (time > 0):
		self.look_at(Vector2(rand_range(0,800),rand_range(0,800)))
		
		if self.get_rotation() != OldRotation:
			CountLooksPerSecond +=1
			
		OldRotation = self.get_rotation()
		
		time -= delta
	else:
		print(CountLooksPerSecond)
		CountLooksPerSecond = 0
		time = 1

The above script prints how many times it updates per second and it is always 60

which I have noticed.

I think what you are seeing is the difference between _process(delta) and _physics_process(delta) if you want to follow a physics object you should be calling it in the physics step.

Yea…you’re correct. I just tried it again and it worked. I think I was confused and had:

self.look_at(target_pos - self_pos, Vector(0,1,0)) 

That vector math page in the docs got me all turned around. Can’t believe it was that simple.

Thanks! Sorry you had to take the time to test it when it was such a silly problem.

tproper | 2018-09-29 00:01

I just want to clear this up because vector math is extremely important for games.
Look at = offset = PointB - PointA

It is a very simple but extremely powerful formula. It provides both the rotation as a vector and the distance to the object as the other part of the vector.
enter image description here

To separate distance from offset we use offset.length() in Godot or Pythagoras theorem.

To separate rotation we use offset.normalized() or offset /offset.length(). A vector rotation is the direction it is pointing at when drawn from zero and looked at from above.

This is very important to know how to do because a lot of AI code like aiming at, runing away from, picking the nearest item, etc will all use this offset formula.

MysteryGM | 2018-09-29 02:28