CameraWorks was a great opportunity to revisit and experiment on some implementations of camera motion behaviours.
Rotation axis independence is a crucial aspect to consider when implementing free camera movement. Normally, FPS controls require camera yaw aligned to world plane’s vertical axis; While flight sim controls require complemetely independent pitch/yaw/roll. A good grasp of quaternion angle and vector math concepts is extremely beneficial for implementing these behaviours.
Case A; To have pitch and yaw aligned to world plane, unaffected by roll: directly append delta rotation to current rotation, set as target rotation.
Case B: To have free pitch, yaw, roll: simply multiply quaternion of current rotation with quaternion of delta rotation.
protected virtual void HandleRotationInput()
{
Vector3 input = CalculateDesiredRotationInput();
smoothedDeltaPitch = Mathf.Lerp(smoothedDeltaPitch, input.x, Time.unscaledDeltaTime * lookSmooth);
smoothedDeltaYaw = Mathf.Lerp(smoothedDeltaYaw, input.y, Time.unscaledDeltaTime * lookSmooth);
smoothedDeltaRoll = Mathf.Lerp(smoothedDeltaRoll, input.z, Time.unscaledDeltaTime * rollSmooth);
if(lookByWorldPlane) {
curEuler += new Vector3(smoothedDeltaPitch, smoothedDeltaYaw, smoothedDeltaRoll);
targetQuaternion = Quaternion.Euler(curAngles);
} else {
deltaQuaternion = Quaternion.Euler(new Vector3(smoothedDeltaPitch, smoothedDeltaYaw, smoothedDeltaRoll));
targetQuaternion *= deltaQuaternion.
}
}
Here independent axis smoothing is achieved by smoothing each axis of rotation input, before composing the final quaternion. Essentially, mouse acceleration.
This may not be desirable in some situations — as the smoothing parameter changes, the same amount of rotation input may result in different amounts of final rotation. For more consistent control of rotation results, we want to first set a definitive target rotation based on rotation input, then smoothly rotate to that target rotation.
Usually something like Quaternion.Slerp is sufficient for smoothing the final rotation, but we still need independent axis smoothing. This can be achieved through eulerangles.
protected virtual void HandleRotationInput()
{
Vector3 input = CalculateDesiredRotationInput();
if(lookByWorldPlane) {
curEuler += input;
targetEuler = curAngles;
} else {
targetRotation *= Quaternion.Euler(input);
}
}
Then we can safely lerp to a well-defined target rotation.
protected virtual void UpdateRotation()
{
if (LookByWorldPlane) {
smoothedEuler.x = Mathf.Lerp(smoothedEuler.x, targetEuler.x, LookSmooth * Time.unscaledDeltaTime);
smoothedEuler.y = Mathf.Lerp(smoothedEuler.y, targetEuler.y, LookSmooth * Time.unscaledDeltaTime);
smoothedEuler.z = Mathf.Lerp(smoothedEuler.z, targetEuler.z, RollSmooth * Time.unscaledDeltaTime);
transform.localEulerAngles = smoothedEuler;
} else {
smoothedQuaternion = Quaternion.Slerp(transform.localRotation, targetRotation, LookSmooth * Time.unscaledDeltaTime);
transform.localRotation = smoothedQuaternion;
}
}
Though independent axis smoothing for Case B is easier achieved with the previous method of smoothing mouse inputs.