I am working on controls for a bipedal tank, that has a boost/dash feature. When the boost/dash mode is activated by holding a button the tank changes from a traditional WASD strafing movement to something like a jet fighter but on ground. Now when the player tries to turn while boosting the camera needs to tilt with the turning direction to make it feel smoother.
Here is a video clip showing my problem
This is the script added to the main camera
public class PlayerCameraTilt : MonoBehaviour
{
private Camera viewCam;
private CharacterController characterController;
public float tiltAmount = 10f;
private void Start()
{
viewCam = GetComponent<Camera>();
characterController = GetComponentInParent<CharacterController>();
}
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, -inputValue, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
else
{
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, 0f, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
}
}
It kind of works, BUT only in one rotation direction, namely the positive one. If i turn the other way the angle becomes negative and does a whole 360 degree rotation until it ends up at 0 degrees.
I tried around with the built in Quaternion methods like Quaternion.Rotate() or Quaternion.AngleAxis() but they didn't work because Quaternion.Rotate() doesn't tilt the Camera but completely rotates it. Quaternion.AngleAxis() works with the whole tilt angle limit but also does something to the other axis which i don't want to modify.
Is there a way to prevent the camera from doing a complete 360° rotation when the axis input becomes negative and just tilt in a slight angle?
Wraparound issues aside, the z component is "applied" first among the euler components, so I wouldn't modify it directly like that in this application.
Instead, I would convert the input to an angle to lean, rotate the direction of the local up before localRotation is applied (Quaternion.Inverse(transform.localRotation) * transform.up
) by that angle, then use Quaternion.LookRotation
to find a rotation with the current forward and that up, then use Quaternion.Slerp
to slerp toward that rotation:
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Vector3 upDir = Quaternion.AngleAxis(-inputValue, transform.forward) * upOfParent;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upDir);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
else
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upOfParent);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
}
Just keep in mind that using t = 5f * Time.deltaTime
for lerp/slerp operations does not always guarantee that the output will ever equal the to
parameter.