Quaternion.Slerp on X and Z axis without Y axis

After further research that lead me along different avenues, I discovered that there were two issues. Both issue's revolved around the fact that the Z-axis was never being normalized to the new Y-axis degree after rotation. @Ruzihm, solved the issue of Rotation. I solved the then visible issue of movement. Which became readily visible once rotation was working properly.

In essence, the Z-axis (transform.forward) must be recalculated after any change in the Y-axis rotation (Vector3.up). Once you have the new normal (transform.forward), the movement vector needed to flattened to the plane to keep the player from diving into the surface of the world. Thank you @Ruzihm for all your assistance.

Here is the new code:

//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
                            Input.GetAxis("Thrust"),
                            Input.GetAxis("Vertical"));

//Normalize the movement direction and flatten the Plane
moveDirection = transform.TransformDirection(moveDirection);
moveDirection = Vector3.ProjectOnPlane(moveDirection, Vector3.up);

moveDirection *= speed;

// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);

// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
                        - Vector3.Dot(transform.forward, Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.position, forwardCurrent * 2, Color.white);

// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);

// rotate based on yaw, then pitch, then roll. 
// This order prevents changes to the projected forward direction

targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);


// Debug to see forward after applying yaw
Debug.DrawRay(transform.position, targetRotation * Vector3.forward, Color.red);

targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);

transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);


controller.Move(moveDirection * Time.deltaTime);

There seem to be some incorrect assumptions about the order of rotations that apply when working with Euler angles. Roll is applied, then pitch, then finally yaw. This means that keeping the same yaw then setting the roll and pitch to zero (or even just changing roll) can completely change the flattened direction you're facing.

It may help to rotate by yaw, flatten the forward direction (aka project it to a completely horizontal plane) Then create a rotation based off that (using Quaternion.LookRotation) which you can then rotate by each axis manually.

if(!controller.isGrounded)
{

    //Three degree's
    moveDirection = new Vector3(Input.GetAxis("Horizontal"), 
                                Input.GetAxis("Thrust"), 
                                Input.GetAxis("Vertical"));
    moveDirection *= speed;

    // collect inputs
    float yaw = Input.GetAxis("Yaw") * rotationSpeed;
    float pitch = Input.GetAxis("Vertical") * tiltAngle;
    float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);

    // Get current forward direction projected to plane normal to up (horizontal plane)
    Vector3 forwardCurrent = transform.forward 
                            - Vector3.Dot(transform.forward,Vector3.up) * Vector3.up;

    // Debug to view forwardCurrent
    Debug.DrawRay(transform.location, forwardCurrent, Color.white, 0f, false);

    // create rotation based on forward
    Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);

    // rotate based on yaw, then pitch, then roll. 
    // This order prevents changes to the projected forward direction

    targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);


    // Debug to see forward after applying yaw
    Debug.DrawRay(transform.location, targetRotation * Vector3.forward, Color.red, 0f, false);

    targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
    targetRotation = targetRotation  * Quaternion.AngleAxis(roll, Vector3.forward);

    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);

    //debug new forward/up
    Debug.DrawRay(transform.location, Transform.forward, Color.blue, 0f, false);
    Debug.DrawRay(transform.location, Transform.up, Color.green, 0f, false);

    controller.Move(moveDirection * Time.deltaTime);
}

This may be considered a partial answer because being able to determine a "flattened forward" direction and reorder the process of applying component rotations is useful to answering your question but may not be enough to get the full effect you want depending on the details.

As a sidenote, you may want to consider using Quaternion.RotateTowards instead of Quaternion.Slerp if you want to ensure that it will actually reach the target rotation instead of infinitely approach it.

Tags:

C#

Unity3D