Zoom to point using orbital/freelook camera in unity (3D)

155 Views Asked by At

I have an interesting yet frustrating problem..

I am creating a "zoom to point" feature in my game, that allows the user to zoom to the mouse cursor in 3D (perspective camera). The cursor should stay on the same xyz position while zooming and moving towards the point. My trouble is achieving the same effect with the freelook/orbital transposer camera in cinemachine. There is a decent chance I'm overthinking it, I nearly went insane yesterday trying to figure this out.

This can normally be achieved by moving the xyz position of the camera in proportion to the delta between the camera position and the cursor location. With a normal camera this is easily done with something like this

mainCamera.transform.position = Vector3.MoveTowards(mainCamera.transform.position, Scrolldirection, scrollwheelValue * step);

Is there a way to shrink/grow the orbital radius of the freelook camera based on a target y position. IE: Camera position is at (100,50,-100) and mouse cursor is at (200,0,-200), I zoom on the position half way, so my new camera position should be (150,25,-150) in order to keep my mouse in the same location. Changing the x and z is easy by simply moving the focal point, the problem is the is changing the y by 25 via the orbital radius and height (this will also change the x and z by unknown amounts), I'm not sure how to do that without calculating some reasonably complex math which I rather avoid. Maybe there is simpler way to do this, or Cinemachine method I haven't found?

I drew a little diagram for y'all orbitals before and after zooming

Appreciate all the help, have a great day!

1

There are 1 best solutions below

0
On

Basically shrinks the entire orbital instead of doing some funky hardcoded trig. Separating the delta y and delta xz into different parts. Theoretically should work, but is missing the ability to manually update the vcam to get it's new position after shrinking it's orbits. If someone knows how then that would be great. :)

public void ZoomTowardPoint2(InputAction.CallbackContext context)
    {
        if (!context.performed)
            return;
       
        float zoomSpeed1 = 50;
        float minZoom = 0;
        float maxZoom = 1000;

        var scrollwheelValue = -context.ReadValue<Vector2>().y;
        Ray ray = mainCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
        RaycastHit point;
        Physics.Raycast(ray, out point, 1000);
        Vector3 Scrolldirection = ray.GetPoint(5);

        float step = zoomSpeed1 * Time.deltaTime;

        // Allows zooming in and out via the mouse wheel
        if (scrollwheelValue > 0 && Scrolldirection.y > minZoom)
        {
            MoveTowardsPointOribital(transform.position, Scrolldirection, scrollwheelValue * step);
        }
        if (scrollwheelValue < 0 && Scrolldirection.y < maxZoom)
        {
            MoveTowardsPointOribital(transform.position, Scrolldirection, scrollwheelValue * step);
        }
    }

private void MoveTowardsPointOribital(Vector3 current, Vector3 target, float maxDistanceDelta)
    {
        //Finds the vector we want to move towards and then gives us a point on that vector
        Vector3 desiredPoint;
        Vector3 a = target - current;
        float magnitude = a.magnitude;
        if (magnitude <= maxDistanceDelta || magnitude == 0f)
        {
            desiredPoint = target;
        }
        desiredPoint = current + a / magnitude * maxDistanceDelta;
      
        float heightDeltaRatio = desiredPoint.y / mainCamera.transform.position.y;
        //adjusts the orbit to bring why down to correct height
        zoomDistance *= heightDeltaRatio;
        UpdateRadiusHeightofRigs();
        mainCamera.GetComponent<CinemachineBrain>().ManualUpdate();
      
        //need to correct for new x and z after updated rigs
        Vector3 finalDesiredChange = desiredPoint - cinemachineFreeLook.transform.position;
        finalDesiredChange = new Vector3(finalDesiredChange.x, 0, finalDesiredChange.z);
        //moves focalPoint which in turn moves Orbital Camera by desired amount while
        focalPointRB.transform.position += finalDesiredChange;
    }

private void UpdateRadiusHeightofRigs()
    {
        for (int i = 0; i < 3; i++)
        {
            cinemachineFreeLook.m_Orbits[i].m_Radius = orbitRadii[i] * zoomDistance;
            cinemachineFreeLook.m_Orbits[i].m_Height = orbitHeight[i] * zoomDistance;
        }
    }