jozza710 jozza710 -4 years ago 141
C# Question

Smooth Rotation - Quaternions

I am trying to make a first person character controller and I am having an issue smoothing my look rotation. My

LookRotation
script takes my mouses input and uses it to rotate the character. I have used
Quaternion.Slerp()
in order to smooth my look rotation although there is an issue with using
Quaternion.Slerp()
. Once the desired rotation becomes larger than 180 degrees away from the current rotation it will just take the shorter route (the opposite way). The issue is in the last two lines of code. Does anyone have a method to prevent this issue? Thanks in advance!

#region Variables
// Sensitivity variables
[Range(0.0f, 10.0f)] public float horizontalSensitivity = 2.0f, verticalSensitivity = 2.0f;

// Smoothing Variables
[Range(0.0f, 30.0f)] public float smoothAmount = 5.0f;

// Character rotation variables
private Quaternion characterTargetRotation;
private Quaternion cameraTargetRotation;

public Transform character;
public Transform characterCamera;
#endregion

private void Update()
{
float horizontalRotation = Input.GetAxis("Mouse X") * horizontalSensitivity;
float verticalRotation = Input.GetAxis("Mouse Y") * verticalSensitivity;

characterTargetRotation *= Quaternion.Euler(0.0f, horizontalRotation, 0.0f);
cameraTargetRotation *= Quaternion.Euler(-verticalRotation, 0.0f, 0.0f);

character.localRotation = Quaternion.Lerp(_character.localRotation, characterTargetRotation, smoothAmount * Time.deltaTime);
characterCamera.localRotation = Quaternion.Lerp(_characterCamera.localRotation, cameraTargetRotation, smoothAmount * Time.deltaTime);
}

Answer Source

Can't be done using Quaternions and Lerp, I solved this using Euler angles.

    horizontalAngle += (Input.GetAxis("Mouse X") * horizontalSensitivity);
    verticalAngle += (-Input.GetAxis("Mouse Y") * verticalSensitivity);

    horizontalSmoothAngle = Mathf.Lerp(horizontalSmoothAngle, horizontalRotation, smoothAmount * Time.deltaTime);
    verticalSmoothAngle = Mathf.Lerp(verticalSmoothAngle , verticalRotation, smoothAmount * Time.deltaTime);

    horizontalRotation = Quaternion.Euler(0, smoothRotation ? horizontalSmoothAngle: horizontalAngle, 0);
    verticalRotation = Quaternion.Euler(smoothRotation ? verticalSmoothAngle : verticalAngle, 0, 0);

Using this method creates alternate issues you must deal with but using Quaternions and Lerp has an issue that cant be solved. As commented above, Lerp will always find the shortest distance. Eulers can go as high as you want (or more precisely as high as the data type allows) so it doesn't have this issue.

Although it is unlikely this will ever be an issue for a normal player if you keep adding to the angle, the data type in this case float, will eventually lose precision. To avoid this simply minus or add (dependent on whether the value is positive or negative) any multiple of 360 from the angle and the smoothed angle at a number high enough as to not effect the Lerp.

Getting the starting angle on the x axis using this method has some issues but is irrelevant to this question so anyone wanting to know how to do that will have to find an alternative source for the solution to that issue.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download