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

2 Likes

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.

2 Likes

Hey everyone!

I am trying to do things with Cinemachine but I can’t get the namespace to work inside VS code.
I keep getting the error that the type of namespace “Cinemachine” could not be found.

I look over the internet for fixes but nothing seems to work for me.

Anyone got any idea why it’s not working?

Thanks!

Hi dramolxe,

You’re not alone. I’ve seen lots of folks have issues with added packages in VS Code lately.
A few things to check:

The first and obvious one is to make sure your Cinemachine package is installed in the Package Manager. While you’re in the Package Manager, make sure that your Visual Studio Code plugin is up to date. There was recently an update to both the VS Code and VS Community package.

Once this is done, in Unity, (With Visual Studio Code closed) open the Preferences dialogue. You’ll see a list of check boxes followed by Regenerate Project Files.
Check any one of those boxes, and then click Regenerate Project Files.
You can then uncheck that box and click Regenerate Project Files again.
image

1 Like

Hey Brian!

Thanks for the info. I’ve tried that and it still wouldn’t work.
I’ve searched all over the internet and saw that many people had this issue and for others the solution you posted worked, but for me it didn’t.

What finally worked for me was clearing my unity project cache.
Going to Edit => Preferences => GI Cache Tab and clicking the clear cache button.

If people that have the same issue can’t seem to find a way to make it work, I hope they try this method as well!

1 Like

The joys of the evolving nature of software. A few months ago, my answer would have been to follow the instructions in the Not Making Intellisense lecture, it would have quite probably worked, but things have changed so much that it isn’t sufficient anymore.
I’ve made a note of this to add to future answers.
I’m glad you got it working!

1 Like

Hey everyone,
Just want to share my approach to camera rotation.


Same as we did on the CoreCombatCourse, but Aim to be set to POV.

To take control of rotation I set speed to 300, if mouse button down, then back to 0, when mouse button is up.

CinemachineVirtualCamera virtualCamera = GetComponent<CinemachineVirtualCamera>();
CinemachinePOV pov = virtualCamera.GetCinemachineComponent<CinemachinePOV>();

void LateUpdate()
{
    if (Input.GetMouseButton(1))
    {
        pov.m_HorizontalAxis.m_MaxSpeed = 300f;
    }
    else
    {
        pov.m_HorizontalAxis.m_MaxSpeed = 0f;
    }
}

P.S.
As you can see I take control only Horizontal and Vertical locked to 40 degree all time.
Thats because I want it to be as in Divinity Original Sin 2.
To make camera “tactical mode”, I just set angle to 90 for Top-Down look.

1 Like

Privacy & Terms