I have a camera script where if my character is rotating, the camera rotates too. However, the camera can also be rotated my the mouse pointer. This movement is restricted by a specific set of given degrees, say 30 (maximumX). So the range of camera movement controlled by the mouse should be between 0 and 60 then which is equivalent to -30 and 30 degrees.
If the character rotates a full 360 degrees, then the maxX value overlaps 360 and has to go back to 0. While min may remain something like 300. This creates an issue where the camera will "snap" and not preserve its previous rotation properly.
var newMaxX = ClampAngle(_target.localEulerAngles.y + maximumX, -360, 360);
var newMinX = ClampAngle(_target.localEulerAngles.y - maximumX, -360, 360);
if(newMinX > newMaxX)
{
var tmp = newMaxX;
newMaxX = newMinX;
newMinX = tmp;
}
if (axes == RotationAxes.MouseXAndY) {
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX = ClampAngle (rotationX, newMinX, newMaxX);
rotationY = ClampAngle (rotationY, minimumY, maximumY);
var xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
var yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);
cameraTransform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
else if (axes == RotationAxes.MouseX)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationX = ClampAngle (rotationX, newMinX, newMaxX);
xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
cameraTransform.localRotation = originalRotation * xQuaternion;
}
else
{
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = ClampAngle (rotationY, minimumY, maximumY);
yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);
cameraTransform.localRotation = originalRotation * yQuaternion;
}
Here's the ClampAngle function:
static function ClampAngle (angle : float, min : float, max : float) : float
{
if (angle < -360.0)
angle += 360.0;
if (angle > 360.0)
angle -= 360.0;
return Mathf.Clamp (angle, min, max);
}
Any recommendations?
The issue I discovered was with using:
_target.localEulerAngles.y
as a base. This is because localEulerAngels only go from 0 to 360 while running. In the editor, it isn't clamped. When running, it is clamped. This changes things dramatically because initially at rotation 0, if you move left, it loops to 360. I was treating it as if it would go to -1. Thus, thinking my math was wrong or I wasn't clamping correctly.
Clamping wouldn't do anything because it wouldn't notice anything about 0 becoming 360.
So, I decided to treat it as -180 to 180 instead.
var eulerTargetY = _target.localEulerAngles.y;
var newMaxX = 0;
var newMinX = 0;
if(eulerTargetY > 180)
{
var adjustedEuler = eulerTargetY - 360;
newMaxX = adjustedEuler + m_rangeX;
newMinX = adjustedEuler - m_rangeX;
}
else
{
newMaxX = eulerTargetY + m_rangeX;
newMinX = eulerTargetY - m_rangeX;
}
var xQuaternion = Quaternion.AngleAxis (rotationX, Vector3.up);
var yQuaternion = Quaternion.AngleAxis (rotationY, Vector3.left);
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = ClampAngle (rotationY, minimumY, maximumY);
rotationX = ClampAngle (rotationX, newMinX, newMaxX);
if (axes == RotationAxes.MouseXAndY) {
cameraTransform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
else if (axes == RotationAxes.MouseX)
{
cameraTransform.localRotation = originalRotation * xQuaternion;
}
else
{
cameraTransform.localRotation = originalRotation * yQuaternion;
}
For clarity, I made maximumX into m_rangeX, because previous I had it as 2 seperate units and I'm using 30 both ways currently. So now, the camera can properly sweep where the player points his mouse between _target.localEulerAngles.y - 30 and _target.localEulerAngles.y + 30 with circular overlap.