Hi everyone!
DESCRIPTION
I am using a script I got from Unity’s forums to move my camera. I tried to zoom in by decreasing the FoV and making the transform look at the cursor. The point I am aiming at is correct as tested using CreatePrimitive() on it.
PROBLEM
At first it works and the camera looks at the right spot at first but then something happens in RotateCameraWithMouse() that makes the camera look at the wrong position. Video 8aiil0
The problem seems to be related to the line I marked with // ************** SEEMINGLY THE PROBLEMATIC LINE *************** //. If I comment it out, the zoom doesn’t throw the aim off but the camera can’t move vertically and the horizontal rotation becomes very sensitive to mouse movements.
SOME OF THE THINGS I TRIED
- Turning mouse smooth off
- Correcting the clamp amount to compensate for the FoV change, which I hadn’t done before
- Changing the
targetDirectionto the post-zoom camera rotation
MINIMUM REPRODUCIBLE EXAMPLE
I know you guys have far better things to do than debug other people’s projects but in case an angel among you is feeling extra bored, I have attached a minimum reproducible example below as a .unitypackage:
Thank you all very much
SCRIPT
using UnityEngine;
public class MouseLook : MonoBehaviour
{
private Vector2 _mouseAbsolute;
private Vector2 _smoothMouse;
public Vector2 initialMaxCameraRotation = new Vector2(40, 30);
public Vector2 currentMaxCameraRotation;
public Vector2 sensitivity = new Vector2(0.1f, 0.1f);
public Vector2 smoothing = new Vector2(3, 3);
private Quaternion initialCameraRotation;
private MainCamera mainCamera;
public bool isZoomActive = false;
private float zoomFOVDelta; // number of degrees subtracted from normal camera's vertical FoV when zooming in
private float zoomFOVValue = 10;
void Start()
{
// limit the camera rotation to where the game takes place
currentMaxCameraRotation = initialMaxCameraRotation;
// initial direction the camera is facing
initialCameraRotation = transform.localRotation;
mainCamera = gameObject.GetComponent<MainCamera>();
// difference in horizontal field of view after zoom is applied
zoomFOVDelta = mainCamera.startingHorizontalFieldOfView - Camera.VerticalToHorizontalFieldOfView(10, mainCamera.mainCamera.aspect);
}
void Update()
{
ZoomIfRightButtonHeldDown();
RotateCameraWithMouse();
}
void ZoomIfRightButtonHeldDown()
{
// ##### START ZOOMING #####
if (Input.GetMouseButton(1))
{
// determine where the mouse was so that we can make the camera look at it
int layerMask = 1 << 8; // ground layer
Ray mouseClick = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
Physics.Raycast(mouseClick, out hitInfo, Mathf.Infinity, layerMask);
// no need to apply zoom multiple times
if (false == isZoomActive)
{
// ##### ZOOM AND LOOK AT POINT #####
mainCamera.mainCamera.fieldOfView = zoomFOVValue;
mainCamera.transform.LookAt(hitInfo.point);
// since the FoV changed, the maximum rotation the camera can perform should change as well so it can see the same area
currentMaxCameraRotation.y += zoomFOVDelta;
currentMaxCameraRotation.x += mainCamera.startingHorizontalFieldOfView - Camera.VerticalToHorizontalFieldOfView(zoomFOVValue, mainCamera.mainCamera.aspect);
//DEBUG: check if this is the point you want to look at and add visual reference to compare
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.position = hitInfo.point;
}
isZoomActive = true;
}
// ##### ZOOM OFF #####
else
{
// change the clamp back since FoV has been increased so it can see the same area
currentMaxCameraRotation = initialMaxCameraRotation;
// change the FoV back to its initial value
mainCamera.mainCamera.fieldOfView = mainCamera.startingVerticalFieldOfView;
isZoomActive = false;
}
}
void RotateCameraWithMouse()
{
// Get raw mouse input for a cleaner reading on more sensitive mice.
var mouseDelta = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
// ##### SMOOTH #####
// Scale input against the sensitivity setting and multiply that against the smoothing value.
mouseDelta = Vector2.Scale(mouseDelta, new Vector2(sensitivity.x * smoothing.x, sensitivity.y * smoothing.y));
// Interpolate mouse movement over time to apply smoothing delta.
_smoothMouse.x = Mathf.Lerp(_smoothMouse.x, mouseDelta.x, 1f / smoothing.x);
_smoothMouse.y = Mathf.Lerp(_smoothMouse.y, mouseDelta.y, 1f / smoothing.y);
// Find the absolute mouse movement value from point zero.
_mouseAbsolute += _smoothMouse;
// ##### CLAMP CAMERA ROTATION #####
// Clamp and apply the local x value first, so as not to be affected by world transforms.
if (currentMaxCameraRotation.x < 360)
_mouseAbsolute.x = Mathf.Clamp(_mouseAbsolute.x, -currentMaxCameraRotation.x * 0.5f, currentMaxCameraRotation.x * 0.5f);
// Then clamp and apply the global y value.
if (currentMaxCameraRotation.y < 360)
_mouseAbsolute.y = Mathf.Clamp(_mouseAbsolute.y, -currentMaxCameraRotation.y * 0.5f, currentMaxCameraRotation.y * 0.5f);
// ##### APPLY FINAL CAMERA ROTATION #####
// ************** SEEMINGLY THE PROBLEMATIC LINE *************** //
transform.localRotation = Quaternion.AngleAxis(-_mouseAbsolute.y, initialCameraRotation * Vector3.right) * initialCameraRotation;
var yRotation = Quaternion.AngleAxis(_mouseAbsolute.x, transform.InverseTransformDirection(Vector3.up));
transform.localRotation *= yRotation;
}
}