# Clamping camera forward and backward vector in Unity3D

My perspective camera is rotated on the x axis. I've implemented a zoom with the scroll wheel and it works great.

I'm trying to limit the zoom, however. My solution right now is to take the relative scale of the transform.forward y and z components and clamp the y and z position of the camera by using Mathf.Clamp and multiplying the minZoom and maxZoom floats by the corresponding transform.forward component. It works well, except it limits the y axis, so when I translate around I can't go below a given y due to my clamp with is ideally meant to affect the zoom only.

Is there a way to clamp the forward and backward movement of the camera? I've been looking everywhere but can't find a thing.

Thanks.

Code

public class ClickManager : MonoBehaviour

{
public float dragSpeed = 2;
public float minZoom = 0.5f;
public float maxZoom = 5.0f;

private Vector3 dragOrigin;

private float lerpPercentBack = 0; // value between 0 and 1 representing percentage of lerp
private float lerpPercentForward = 0;
private bool isLerpingBack = false;
private bool isLerpingForward = false;

void Update()
{
float scroll = Input.GetAxis("Mouse ScrollWheel");

if (Input.GetMouseButtonDown(1))
{
dragOrigin = Input.mousePosition;
return;
}

if (scroll > 0)
{
isLerpingForward = true;
lerpPercentForward = 0;
}
if (scroll < 0)
{
isLerpingBack = true;
lerpPercentBack = 0;
}

if (isLerpingBack)
{
scrollLerperBack();
lerpPercentBack += 0.05f;

if (lerpPercentBack >= 1.0f)
{
lerpPercentBack = 0;
isLerpingBack = false;
}
}

if (isLerpingForward)
{

scrollLerperForward();
lerpPercentForward += 0.05f;

if (lerpPercentForward >= 1.0f)
{
lerpPercentForward = 0;
isLerpingForward = false;
}
}

//Debug.Log(lerpPercent);

/*if (!Input.GetMouseButton(1)) return;

Vector3 pos = Camera.main.ScreenToViewportPoint(Input.mousePosition - dragOrigin);
Vector3 move = new Vector3(pos.x * dragSpeed, pos.y * dragSpeed, 0);

transform.Translate(move, Space.World);

Debug.Log(scroll);*/

}

void scrollLerperBack()
{
transform.position -= Vector3.Lerp(transform.forward, Vector3.zero, lerpPercentBack);
}

void scrollLerperForward()
{
transform.position += Vector3.Lerp(transform.forward, Vector3.zero, lerpPercentForward);
}

void LateUpdate()
{
Vector3 pos = transform.position;

pos.y = Mathf.Clamp(pos.y, minZoom * transform.forward.y,
maxZoom * transform.forward.y);
pos.z = Mathf.Clamp(pos.z, minZoom * transform.forward.z,
maxZoom * transform.forward.z);
transform.localPosition = pos;
}

}


scroll value resets when it's not scrolling. For one time scrolling the value turns +/- 0.1f. For multiple time scrolling at a time it changes +/- 0.1f on the current scroll value. Else it resets to Zero on next frame. That's why scrollCount will be used as scroll counter.

Two new variables:

private float scrollCount = 0f; //scroll count is related to max zoom

[SerializeField]
private float maxZoomLevel = 0.2f; //so zoom levels are -0.2f, -0.1f, 0.0f, 0.1f, 0.2f


Changes in Update() which will replace your if (scroll > 0) and if (scroll < 0) blocks of code.

    if ((Mathf.Abs(scroll) == 0.1f && //Checking for one time scrolls only
scrollCount < maxZoomLevel + 0.1f && scrollCount > -maxZoomLevel - 0.1f)) //Margins on max and min zoom level
{
if (scroll == 0.1f && scrollCount < maxZoomLevel) //Zoom In
{
scrollCount += 0.1f;

isLerpingForward = true;
lerpPercentForward = 0;

isLerpingBack = false;
lerpPercentBack = 0;
}

if (scroll == -0.1f && scrollCount > -maxZoomLevel) //Zoom Out
{
scrollCount -= 0.1f;

isLerpingForward = false;
lerpPercentForward = 0;

isLerpingBack = true;
lerpPercentBack = 0;
}
}


Get rid of your FixedUpdate() statements and enable the code at the end of Update() regarding Vector3 pos and Vector3 move.

There is another solution for Zoom. You may use Camera.main.fieldOfView for perspective camera. Field of view is by default 60f. Less than 60f will zoom in and greater than 60f will zoom out. For little amount of zoom changing field of view is harmless.

• Thanks for this. I'm getting some problems with the zoom not properly maintaining its minimum and maximum. I think I'll use field of view, it seems a lot easier. Jul 14, 2016 at 15:48
//
//Filename: maxCamera.cs
//
// original: http://www.unifycommunity.com/wiki/index.php?title=MouseOrbitZoom
//
// --01-18-2010 - create temporary target, if none supplied at start

using UnityEngine;
using System.Collections;

public class maxCamera : MonoBehaviour
{
public Transform target;
public Vector3 targetOffset;
public float distance = 5.0f;
public float maxDistance = 20;
public float minDistance = .6f;
public float xSpeed = 200.0f;
public float ySpeed = 200.0f;
public int yMinLimit = -80;
public int yMaxLimit = 80;
public int zoomRate = 40;
public float panSpeed = 0.3f;
public float zoomDampening = 5.0f;

private float xDeg = 0.0f;
private float yDeg = 0.0f;
private float currentDistance;
private float desiredDistance;
private Quaternion currentRotation;
private Quaternion desiredRotation;
private Quaternion rotation;
private Vector3 position;

void Start() { Init(); }
void OnEnable() { Init(); }

public void Init()
{
//If there is no target, create a temporary target at 'distance' from the cameras current viewpoint
if (!target)
{
GameObject go = new GameObject("Cam Target");
go.transform.position = transform.position + (transform.forward * distance);
target = go.transform;
}

distance = Vector3.Distance(transform.position, target.position);
currentDistance = distance;
desiredDistance = distance;

//be sure to grab the current rotations as starting points.
position = transform.position;
rotation = transform.rotation;
currentRotation = transform.rotation;
desiredRotation = transform.rotation;

xDeg = Vector3.Angle(Vector3.right, transform.right );
yDeg = Vector3.Angle(Vector3.up, transform.up );
}

/*
* Camera logic on LateUpdate to only update after all character movement logic has been handled.
*/
void LateUpdate()
{
// If Control and Alt and Middle button? ZOOM!
if (Input.GetMouseButton(2) && Input.GetKey(KeyCode.LeftAlt) && Input.GetKey(KeyCode.LeftControl))
{
desiredDistance -= Input.GetAxis("Mouse Y") * Time.deltaTime * zoomRate*0.125f * Mathf.Abs(desiredDistance);
}
// If middle mouse and left alt are selected? ORBIT
else if (Input.GetMouseButton(2) && Input.GetKey(KeyCode.LeftAlt))
{
xDeg += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
yDeg -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;

////////OrbitAngle

//Clamp the vertical axis for the orbit
yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
// set camera rotation
desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
currentRotation = transform.rotation;

rotation = Quaternion.Lerp(currentRotation, desiredRotation, Time.deltaTime * zoomDampening);
transform.rotation = rotation;
}
// otherwise if middle mouse is selected, we pan by way of transforming the target in screenspace
else if (Input.GetMouseButton(2))
{
//grab the rotation of the camera so we can move in a psuedo local XY space
target.rotation = transform.rotation;
target.Translate(Vector3.right * -Input.GetAxis("Mouse X") * panSpeed);
target.Translate(transform.up * -Input.GetAxis("Mouse Y") * panSpeed, Space.World);
}

////////Orbit Position

// affect the desired Zoom distance if we roll the scrollwheel
desiredDistance -= Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs(desiredDistance);
//clamp the zoom min/max
desiredDistance = Mathf.Clamp(desiredDistance, minDistance, maxDistance);
// For smoothing of the zoom, lerp distance
currentDistance = Mathf.Lerp(currentDistance, desiredDistance, Time.deltaTime * zoomDampening);

// calculate position based on the new currentDistance
position = target.position - (rotation * Vector3.forward * currentDistance + targetOffset);
transform.position = position;
}

private static float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
}