2
\$\begingroup\$

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;
    }

}
\$\endgroup\$
1

2 Answers 2

1
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – duncster94
    Jul 14, 2016 at 15:48
0
\$\begingroup\$
//
//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;


[AddComponentMenu("Camera-Control/3dsMax Camera Style")]
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);
    }
}
\$\endgroup\$

You must log in to answer this question.

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