I have a character
that has a velocity value,
but I want to try make it be able to lean towards the current velocity value
like in this video: https://youtu.be/SAtwQa8t_3g?t=31
The problem is I don`t know how I would go about doing this.
Any help is appreciated!

Godot version 3.4.4.stable
in Engine

+1 vote

If you're okay with the lean being instantaneous instead of smooth, then `rotate(axis, lean_angle)` should do the trick. The lean angle can be 30 degrees, or PI/6 radians. The rotation axis must be perpendicular to both the velocity and the up direction. So `var axis = velocity.cross(Vector2(0,1,0))` should do the trick. We used the vector cross product to find a vector perpendicular to two other vectors.

A smoother lean can be achieved by interpolating quaternions, which is usually the best way of interpolating rotations because it doesn't result in a jaggedy rotation (unlike interpolating Euler rotation vectors). I'll give a sketch, but there might be a mistake or two. All the following code should be in `_physics_process` so that it's not dependent on framerate, and should be on a Spatial node.

``````# Find the current quaternion from the current transform.
var current_quat = transform.basis.get_rotation_quat()

# Find the quaternion you want to interpolate towards based on the current velocity
# and the maximum lean angle (say PI / 6 radians).
var desired_quat = Quat(velocity.cross(Vector2(0,1,0)).normalized(), PI / 6)

# Calculate an interpolated quaternion (using so-called spherical linear interpolation).
var next_quat = current_quat.slerp(desired_quat, 0.5)

# Make the character lean by updating the character's transform.
transform.basis = Basis(next_quat)
``````

The 0.5 means `next_quat` will be halfway between `current_quat` and `desired_quat`. Decreasing this value closer to 0.0 makes the lean slower, and increasing the value closer to 1.0 will makes the lean faster. But no matter what the lean will start faster than it ends (i.e. ease out but not ease in).

So that is a sketch of how to interpolate rotations with quaternions. It's supposed to give you a smooth lean even when the player is turning towards a new direction.

Feel free to comment further questions.

by (180 points)
edited by

Thanks dude! Thank you for including the awnser and an explination!

One last question! if I wanted the character also rotate towards the Up and Down movement (i.e. jumping and falling down). how would I need to change the current script to do that?

Okay no problem. Let's break down how the leaning works with the jumping in the video you linked.

Firstly, if the character wasn't moving forwards when they jumped then there wasn't any forward or backward lean. Secondly, if the character jumped while moving forwards, then the forward lean transitioned into a backward lean near the end of the jump. So let's aim to make a jump that will transition from a forward lean to a backward lean if the character was moving forwards when they jumped.

Keep three boolean variables `var going_forward = false`, `var going_up = false`, and `var going_down = false` that you use to keep track of whether the player is going forward, upwards, or downwards.

Set the values every physics cycle

``````if is_zero_approx(Vector2(velocity.x, velocity.z).length()):
going_forward = false
else:
going_forward = true
if is_zero_approx(velocity.y):
going_up = false
going_down = false
else:
if velocity.y > 0:
going_up = true
going_down = false
else:
going_up = false
going_down = true
``````

Also keep a constant `const LEAN_ANGLE = PI / 6`.

Use the booleans to influence the desired lean angle value like this

``````var desired_lean_angle = int(going_forward) * (1 - int(going_up) - 2 * int(going_down)) * LEAN_ANGLE
var desired_quat = Quat(velocity.cross(Vector2(0,1,0)).normalized(), desired_lean_angle)
``````

The `desired_lean_angle` calculation assigns the same value as the following if statements would

``````if not going_forward:
desired_lean_angle = 0.0
else:
if going_up:
desired_lean_angle = 0.0
elif going_down:
desired_lean_angle = -LEAN_ANGLE
else:
desired_lean_angle = LEAN_ANGLE
``````

One issue with this is that it will jerk into the backwards lean near the top of the jump. I suppose a more advanced approach would be to make the animations for the forward movement and the jump/fall separately, and then blend them together using the various animation nodes available in Godot. But I'm not sure how to do that.

That has pointed me in the right direction for making the lean to work! Thank you so much for the help you`ve given me, I would have not been able to do this for my game without you help!