Help projecting/rotating input vector onto normal

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

I have an fps controller that I’m attempting to add a wall running feature too.

when on the wall I need to take my input direction Vector3 that currently only has values on the x and z axis for ground movement and rotate this vector to match the walls normal to move along this wall.

I’ve made several attempts but my vector powers are not great enough. thanks in advance!

edit:

to better explain the problem if the user has an input vector3 of (0,0,1) “forward” and a collision occurs with the normal of (0, 0, -1) I need to make the input vector now rotate such that the collision normal is now “up” and opposite to that is “down” in this case the rotated input vector would be (0,1,0) so that the player would now move up the wall.

you can imagine a bug with forward movement colliding into a wall and is now moving up along the wall.

:bust_in_silhouette: Reply From: Andrea

change x and z input to wall.global_transform.basis.z and .x(or x and y, or y and z, depending on how you set up the wall)

this solution would not work in my case as the level the player is colliding with will always have a basis of IDENTITY since the level is never rotated after loaded in. I need a way to take a normal value from a collision and rotate my input to match that. not based on the levels / walls rotation.

adoci | 2021-01-13 17:16

i just read the edit, but it’s not clear yet.
how did you define the player movement? Is the forward Vector3(0,0,1) in world space or player space?

If it is in player space, simply rotate the player so that the new “up” is now the collision normal ( https://kidscancode.org/godot_recipes/3d/3d_align_surface/ in your case, new_y is the collision_normal result)

    player.transform.basis.y = collision_normal
    player.transform.basis.x = -player.tranform.basis.z.cross(collision_normal)
    player.transform.basis = player.transform.basis.orthonormalized()

After rotated, the forward vector remains the same

If it is in world space, the player does not rotate, but the forward/backward/up/down/left/right (let’s call them direction vectors) need to be changed to a new vector set that is oriented on the collision_normal.
If your level is designed with multiple separated and straight wall, the collision normal is equivalent to one of the wall’s transform.basis (the one perpendicular to the flat exposed surface), so you can use the basis itself to define the direction vectors (this is what i assumed in my first answer)

Otherwise you can have a transform approach, creating your own direction_transform, and rotate it on the collision direction (similarly as above). After that, the forward/backward input are simply

forward=direction_transform.basis.z
backward=-direction_transform.basis.z
left=direction_transform.basis.x
etc.

Andrea | 2021-01-13 18:10

I was able to get make a direction_tranform that I could rotate but led to issues with having to know the axis of rotation for each normal of each collision that took place since the player doesn’t rotate only the direction I wish to move the player rotates. but lead me to an answer where instead I utilized Quat’s to handle the rotation and find the angle and axis need to rotate a vector that aligns with the plane being walked on. so thanks!

func rotate_to_plane(vector: Vector3, normal: Vector3) -> Vector3:
var rotation_direction = Plane(Vector3.UP, 0).project(normal)
var rotation = Quat.IDENTITY
rotation.set_axis_angle(Vector3.UP, deg2rad(-90))
rotation_direction = rotation.xform(rotation_direction)
var angle = -Vector3.UP.angle_to(normal)
rotation = Quat.IDENTITY
rotation.set_axis_angle(rotation_direction.normalized(), angle)
vector = rotation.xform(vector)

return vector

adoci | 2021-01-13 22:16