Smoothly rotate node towards an angle
Asked Answered
T

10

0

In my Player node I want to rotate the node that has the model, so it faces the current direction at all times. I've been using look_at() but the transition is immediate and it's not smooth.

I have the following code for now, but I can't figure out how to smoothly apply the calculated angle to the current rotation of the node:

if direction != _last_direction:
	var target = position + direction

	var diff_angle = direction.angle_to(position)

	# Normalize the difference angle to range -PI to PI
	while diff_angle < -PI:
		diff_angle += 2 * PI
	while diff_angle > PI:
		diff_angle -= 2 * PI

	# Rotate ??

	_last_direction = direction

In this code:

  • direction: Vector3 with the current facing direction
  • _last_direction: Vector3 of the previous facing direction
  • target: the desired Vector3 to face, with this I calculate the angle that's stored in diff_angle

How can rotate a given node smoothly to face the target?

Transmissible answered 7/7, 2023 at 9:54 Comment(0)
G
0

Perhaps this topic will help you.

Gharry answered 7/7, 2023 at 10:45 Comment(0)
T
0

Gharry Yes I could use a Tween, but first I need to calculate the rotation to apply, which is the key part that I'm missing.

Transmissible answered 7/7, 2023 at 16:26 Comment(0)
T
0

Transmissible
Use look_at(TARGET)
To get a vector3 pointing towards the target
Use that as the destination for a tween

Tammara answered 7/7, 2023 at 18:5 Comment(0)
T
0

Tammara the look_at() method does not return a Vector3. In fact, I already have the Vector3 that points to the target. I have tried to use that target as the destination of the Tween and it doesn't work, the model is rotating in strange ways.

Transmissible answered 7/7, 2023 at 18:15 Comment(0)
E
0

Transmissible Gradually spherically interpolate between starting and wanted orientation quaternions using Quaternion::slerp(). You can animate the interpolation parameter either manually in _process() or using a tween. The latter is better if you also need to apply some easing.

Eadmund answered 7/7, 2023 at 18:31 Comment(0)
L
0

HI ...
do this is _process or _physics_process ... if used in _process multiply amount by delta

transform.basis = transform.basis.slerp( transform.looking_at( look_at_position ).basis, amount_of_rotation )

where amount_of_rotation is between 0 and 1

Linlithgow answered 8/7, 2023 at 12:27 Comment(0)
M
0

hehe I am still mess around with this, the motion of rotation of the objects I am trying to align with the target node's rotation is rather hilarious -.-

1234.mp4
2MB

hmmm I messed with klaas's method a bit, and it seemed to work now:

transform.basis = target_node.transform.basis.slerp( transform.looking_at( position ).basis, 0.01 )

Melquist answered 8/7, 2023 at 14:33 Comment(0)
L
0

hmmm ... this seems odd ... you shouldn't use target_node.transform.basis.slerp

The idea is ... transform.basis (rotation) is equal current rotation ( transform.basis ) slerped (interpolated) to rotation after a look_at ( transform.looking_at( position_of_node_to_look_at).basis )

Linlithgow answered 8/7, 2023 at 15:6 Comment(0)
B
0

Found a solution for a similar problem
I am dealing with a smooth rotation on angle aswell.
There the links of mine topics related to this:

https://mcmap.net/q/310/godot-script-of-camera-and-movements-limits
https://mcmap.net/q/1042/godot-realistic-change-of-movement-direction

Benignant answered 4/8, 2023 at 0:39 Comment(0)
S
0

I have figured out a solution:

1) This is not perfect, see my (2) below.

Without any speed_factor (interpolating by default amount):

func _process(delta):

    var angle_to_target = int(    rad_to_deg(  get_angle_to(target_body)  )    )

    if(angle_to_target > 0): #rotate counter-clockwise if the target is on your left
	global_rotation += 1 * delta
    else: #rotate clockwise if the target is on your right
	global_rotation -= 1 * delta

This is not perfect!
Basically, if our angle to target is not 0 (we aren't facing it perfectly), we'll rotate ourselves towards it by unit amount every frame, thus gradually correcting our angle towards it. This means every frame, our angle_to_target will reduce because we're gradually turning towards it. However, if our angle_to_target is very close but still greater than zero, rotating by + (1 * delta) may overcorrect our angle resulting in angle_to_target becoming < 0 (we overdid our rotation towards it, and now we have to rotate back). In that case, the else block will execute and we'll rotate back by - (1 * delta) which may again overcorrect our angle to positive direction requiring us to rotate in the opposite direction again, and so on...
This will result in a "jitter/vibration" as our body closes in on facing the target but can never reduce the angle perfectly to 0 (it'll either overcorrect it in the positive direction, or in the negative direction).
To solve that, we should take a range of rotation values which is greater than the amount we are correcting it by, and if our angle reaches that range, instead of interpolating, we simply look_at(target_body) directly.

2) In my updated code, not only are we addressing the jitter, we are also using an optional speed factor to rotate it faster or slower as we wish:
func _process(delta):

var speed_factor = 5
var angle_to_target = int(    rad_to_deg(  get_angle_to(target_body)  )    )

#lets take an angle range of 2 to -2 degrees (just make sure this range is bigger than the amount of rotation you're doing with adding/subtracting delta * speed_factor to our global_rotation, otherwise you'll get the jitter again)
#So,
if(angle_to_target <= 2 and angle_to_target >= -2): #if its close enough, we don't need to interpolate, just look at it directly, so no chance of overcorrection 
	look_at(target_body)

elif(angle_to_target > 2): #rotate counter-clockwise
	global_rotation += 1 * delta * speed_factor

else: #i.e., if angle_to_target < -2,  #rotate clockwise
	global_rotation -= 1 * delta * speed_factor

WARNING: Make sure your angle range (here 2 to -2 deg) is greater than delta * speed_factor to avoid jitter/overcorrection. If you're not using speed_factor, make sure your angle range is higher than delta to avoid jitter/overcorrection.
Still, even this is not perfect, since the speed_factor isn't effective when we use look_at(), but its good enough for me.

Sines answered 20/3, 2024 at 17:29 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.