Mouse Wheel Zoom Script

So I put Cinemachine in long time ago for a follow camera, but what was still nagging at me was there was no way to change the zoom during play… I got into the next section and just couldn’t take it anymore so here’s a script what will let you zoom in and out with the mouse wheel… if we don’t eventually cover a rotate camera, I will make one to post against this lecture in the future…

the script needs to be called ScrollZoom.cs
place this script on the vcam pointed at your player
make sure your vcam is using “Framing Transposer” in the body section.

EDIT- the script now includes reducing your lookahead time when you scroll in close to your player. If the lookahead time is set to zero, no change will happen. If you have a lookahead time you like when you are fully zoomed out, the script will now reduce that lookahead time when you zoom in to keep your player in frame better.

NOTE - once you have a lookahead time you like, uncheck “Save During Play” or it will continuously override your favorite lookahead time whenever you exit play mode. This is not necessary if your lookahead time is 0, as no change will take place anyway.
(You can always revert it from your prefab if you forget this step)

using UnityEngine;
using Cinemachine;

public class ScrollZoom : MonoBehaviour
{
    CinemachineVirtualCamera vcam;
    CinemachineFramingTransposer framingTransposer;

    [SerializeField] float startingCameraDistance = 10;
    [Space(5)]
    [SerializeField] float minCameraDistance = 5;
    [SerializeField] float maxCameraDistance = 40;
    [Space(10)]
    [Range(0.025f, 1f)]
    [SerializeField] float scrollSensitivity = 0.5f;
    public bool negateScrollDirection = false;
    float maxLookaheadTime;
    float lookaheadAdjusted;

    void Start()
    {
        vcam = GetComponent<CinemachineVirtualCamera>();
        framingTransposer = vcam.GetCinemachineComponent<CinemachineFramingTransposer>();
        framingTransposer.m_CameraDistance = startingCameraDistance;
        maxLookaheadTime = framingTransposer.m_LookaheadTime;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.mouseScrollDelta.y != 0)
        {
            var scroll = Input.mouseScrollDelta.y;
            var adjustedScroll = scroll * scrollSensitivity;
            NewCameraDistance(adjustedScroll);
        }
    }

    private void NewCameraDistance(float adjustedScroll)
    {
        if (negateScrollDirection)
        {
            framingTransposer.m_CameraDistance =
                Mathf.Clamp(framingTransposer.m_CameraDistance += adjustedScroll, minCameraDistance, maxCameraDistance);
        }
        else
        {
            framingTransposer.m_CameraDistance =
                Mathf.Clamp(framingTransposer.m_CameraDistance -= adjustedScroll, minCameraDistance, maxCameraDistance);
        }

        if (maxLookaheadTime == 0) return;
        lookaheadAdjusted = Mathf.Clamp(framingTransposer.m_CameraDistance / maxCameraDistance, 0.1f, maxLookaheadTime);
        framingTransposer.m_LookaheadTime = lookaheadAdjusted;
    }
}
2 Likes

Awesome code!

1 Like

This is well done and more to the point, works with Cinemachine, which is hard to work with.

1 Like

Ok, I’ve tried making camera rotation several times, but today, I struck gold…
First… you need to create a gameObject, I called it Gimble. Child the FollowCamera to the new GameObject.
Then, on the Gimble, I created a script GimbleRotator.

public class GimbleRotator : MonoBehaviour
{
    [SerializeField] float rotationSpeed=10;
    float desiredRotation = 0;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(1))
        {
            desiredRotation += 90;
            if (desiredRotation > 360) desiredRotation = 0;
        }

        float currentRotation = transform.eulerAngles.y;
        if (currentRotation < 0) currentRotation += 360; //normalize direction.
        currentRotation = Mathf.Lerp(currentRotation, desiredRotation, rotationSpeed * Time.deltaTime);
        transform.eulerAngles=new Vector3(0,currentRotation, 0);
    }
}

The gimble doesn’t need to follow the player or anything. Most of my attempts have involved childing the follow camera to the player… which with Cinemachine is a disaster.
It’s currently a bit wierd when you rotate from 270 to 360… it’s going to go the long way round. This is fixable, I just haven’t yet. Ideally, say in a mobile game, you’d put two little rotation arrows one on each side of the screen to go -90 or 90.

1 Like

updated script to reduce lookahead time if one is set so the player stays in frame better when zooming in

1 Like

I’ve made 2 scripts to make the camera rotation.
I added @HeathClose your code to be just one script for me

here is my code, first script is called TopDownCam.cs and the second is called CameraControl.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;

public class TopDownCam : MonoBehaviour
{
    [Header("General")]
    public Transform target;
    public float height;
    public float depth;
    public float yTargetOffset;

    [Header("Rotation Controls")]
    public float yRotOffset;
    public bool enableUserRotation = true;
    public float rotationSpeed = 5;
    public bool hideCursorOnRotate = true;

    [Header("Zoom Controls")]
    [SerializeField] float startingCameraDistance = 10;
    [Space(5)]
    [SerializeField] float minCameraDistance = 5;
    [SerializeField] float maxCameraDistance = 40;
    [Space(10)]
    [Range(0.025f, 1f)]
    [SerializeField] float scrollSensitivity = 0.5f;

    public bool negateScrollDirection = false;

    float maxLookaheadTime;
    float lookaheadAdjusted;

    CameraControl controller;
    CinemachineVirtualCamera vcam;
    CinemachineFramingTransposer framingTransposer;

    void Start()
    {
        //Init variables
        controller = new GameObject().AddComponent<CameraControl>();
        controller.target = target;
        controller.cam = this;

        controller.yTargetOffset = yTargetOffset;
        transform.parent = controller.transform;
        controller.transform.rotation = Quaternion.Euler(new Vector3(0, yRotOffset, 0));

        /// Camera zoom
        vcam = GetComponent<CinemachineVirtualCamera>();
        framingTransposer = vcam.GetCinemachineComponent<CinemachineFramingTransposer>();
        framingTransposer.m_CameraDistance = startingCameraDistance;
        maxLookaheadTime = framingTransposer.m_LookaheadTime;
    }

    void LateUpdate()
    {
        //Update camera position      
        transform.localPosition = new Vector3(0, height, -depth);

        //Update camera rotation
        transform.LookAt(controller.transform);

        if (Input.mouseScrollDelta.y != 0)
        {
            var scroll = Input.mouseScrollDelta.y;
            var adjustedScroll = scroll * scrollSensitivity;
            NewCameraDistance(adjustedScroll);
        }
    }

    private void NewCameraDistance(float adjustedScroll)
    {
        if (negateScrollDirection)
        {
            framingTransposer.m_CameraDistance =
                Mathf.Clamp(framingTransposer.m_CameraDistance += adjustedScroll, minCameraDistance, maxCameraDistance);
        }
        else
        {
            framingTransposer.m_CameraDistance =
                Mathf.Clamp(framingTransposer.m_CameraDistance -= adjustedScroll, minCameraDistance, maxCameraDistance);
        }

        if (maxLookaheadTime == 0) return;
        lookaheadAdjusted = Mathf.Clamp(framingTransposer.m_CameraDistance / maxCameraDistance, 0.1f, maxLookaheadTime);
        framingTransposer.m_LookaheadTime = lookaheadAdjusted;
    }
}
using UnityEngine;
using System.Collections;

public class CameraControl : MonoBehaviour
{

    public Transform target;
    public TopDownCam cam;
    public bool trackPlayer = true;
    public Vector3 lastRot;
    public float smoothTime = 0.3F;
    private Vector3 velocity = Vector3.zero;
    public float panSpeed;
    public float yTargetOffset;

    public Vector3 speed;
    private Vector3 avgSpeed;
    private bool isDragging = false;


    void Start()
    {
        transform.position = new Vector3(target.position.x, target.position.y + cam.yTargetOffset, target.position.z);
    }

    void LateUpdate()
    {
        Vector3 pos = new Vector3(target.position.x, target.position.y + cam.yTargetOffset, target.position.z);


        //Update position
        if (trackPlayer)
            transform.position = Vector3.SmoothDamp(transform.position, pos, ref velocity, smoothTime * Time.deltaTime);


        //User camera rotation
        if (cam.enableUserRotation && trackPlayer)
        {
            if (Input.GetMouseButtonDown(1))
            {
                isDragging = true;

                if (cam.hideCursorOnRotate)
                    Cursor.visible = false;
            }


            if (Input.GetMouseButton(1))
            {
                if (isDragging)
                {
                    speed = new Vector3(-Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"), 0.0F);
                    avgSpeed = Vector3.Lerp(avgSpeed, speed, Time.deltaTime * 5);
                }

            }
            else
            {
                if (isDragging)
                {
                    Cursor.visible = true;
                    speed = avgSpeed;
                    isDragging = false;
                }

                float i = Time.deltaTime * 5;
                speed = Vector3.Lerp(speed, Vector3.zero, i);
            }


            transform.Rotate(transform.up * -speed.x * cam.rotationSpeed, Space.World);
        }
    }
}

let me know what do you think.

PS. this script is based on unity assets.

1 Like