0
\$\begingroup\$

I'm trying to write a camera script in Unity that would smoothly rotate the camera over time towards the desired rotation, with separate input parameters for yaw, pitch and roll smoothing, and I can't figure out how to do it properly. That camera would be meant to follow a vehicle that can move and rotate in 3D space. I've tried many things, most recently the code below:

Vector3 targetLookDirection = (lookTarget - cameraPosition).normalized;
Quaternion targetCameraRotation = Quaternion.LookRotation(targetLookDirection, vehicleTransform.up);

Vector3 currentDeviation = (transform.rotation * Quaternion.Inverse(targetRotation)).eulerAngles;
//since deviation * target = current, then deviation = current * inv(target)
Vector3 newDeviation = new Vector3
{
    x = Mathf.SmoothDampAngle(currentDeviation.x, 0f, ref pitchVelocity, pitchSmoothTime),
    y = Mathf.SmoothDampAngle(currentDeviation.y, 0f, ref yawVelocity, yawSmoothTime),
    z = Mathf.SmoothDampAngle(currentDeviation.z, 0f, ref rollVelocity, rollSmoothTime)
};
transform.rotation = targetRotation;
transform.Rotate(newDeviation);

where lookTarget and cameraPosition are Vector3s, and transform refers to the camera transform since the script is attached to the camera Game Object. The code is in LateUpdate().

The code looks logical to me, but in reality when I run it with the Smooth Times set to around 0.25 the camera just shakes chaotically. The shaking behaviour is also different depending on which way I look in 3D space (with visibly different behaviour when looking towards z+ direction), which is weird to me since the smoothing is only working with relative rotation of the camera from the target rotation, and not their world rotations.

I would very much appreciate it if you could let me know how to fix this script to make it work properly.

\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

Quaternions are hard. Usually to avoid gimbal lock and to make life easier I would advice to split up the camera arm. So one transform is doing x and z rotation while the other transform, its child, is doing y in local space.

I can recommend the asset DoTween, there is a free version, it makes interpolation quite easy you could just say transform.DoLookAt and say how long it should take as an argument. DoTween can also just rotate on one axis.

\$\endgroup\$
1
  • \$\begingroup\$ DoTween sounds like an interesting option, for now I used the Cinemachine Transposer body algorithm which contains an option to smoothly rotate camera with separate pitch, yaw and roll smoothing, and I managed to fix my problem by putting my script on a Cinemachine virtual camera and integrating it with the Transposer smoothing algorithm. When I have time I'll try digging into Cinemachine source code to figure out how it does it so I could put similar logic in my camera script and make it work without relying on Cinemachine. \$\endgroup\$
    – Kirby5
    Dec 25, 2021 at 21:43
0
\$\begingroup\$

I fixed the problem. I changed the last two lines of the code.

Instead of:

transform.rotation = targetRotation;
transform.Rotate(newDeviation);

I wrote:

Quaternion newDeviationQuat = Quaternion.Euler(newDeviation);
transform.rotation = newDeviationQuat * targetRotation;

And now it works perfectly. Don't ask me why.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .