Part of these game systems:
intermediate Touch & Mobile

Pinch-to-Zoom Camera

Smooth pinch-to-zoom camera control for mobile devices supporting both orthographic and perspective cameras. Features configurable zoom limits and optional smoothing.

Unity 2022.3+ · 3.0 KB · PinchToZoomCamera.cs

How to Use

1

Attach the PinchToZoomCamera script to your Main Camera

2

Set minZoom and maxZoom limits in the inspector

3

Enable autoDetectMode to automatically configure for orthographic or perspective

4

Adjust zoomSpeed and smoothSpeed to taste

5

Test with two-finger pinch on a mobile device or use Remote in the editor

Features

  • Smooth pinch-to-zoom
  • Configurable zoom limits
  • Works with orthographic and perspective cameras
  • Zoom speed multiplier
  • Optional zoom smoothing

When to Use This

Perfect for mobile strategy games, RTS, puzzle games, and map views that need intuitive zoom controls. Use for any mobile game where players need to zoom in and out of a 2D or 3D scene — tower defense overviews, photo viewers, or level editors.

Common Mistakes

If autoDetectMode is enabled, it overrides your minZoom/maxZoom values on Awake — disable it when you need custom limits. The zoom feels different on orthographic (changes size) vs perspective (changes FOV) cameras; for perspective cameras that move forward instead of changing FOV, you'll need to modify ApplyZoom. Mouse scroll fallback uses a 100x multiplier that may need adjustment for your scroll wheel sensitivity.

Source Code

PinchToZoomCamera.cs
C#
using UnityEngine;

public class PinchToZoomCamera : MonoBehaviour
{
    [Header("Zoom Settings")]
    [SerializeField] private float zoomSpeed = 0.5f;
    [SerializeField] private float minZoom = 2f;
    [SerializeField] private float maxZoom = 20f;

    [Header("Smoothing")]
    [SerializeField] private bool enableSmoothing = true;
    [SerializeField] private float smoothSpeed = 8f;

    [Header("Camera Mode")]
    [SerializeField] private bool autoDetectMode = true;

    private Camera cam;
    private float targetZoom;
    private float previousTouchDistance;
    private bool isPinching;

    private void Awake()
    {
        cam = GetComponent<Camera>();
        if (cam == null)
            cam = Camera.main;

        if (autoDetectMode)
        {
            minZoom = cam.orthographic ? 1f : 20f;
            maxZoom = cam.orthographic ? 30f : 100f;
        }

        targetZoom = cam.orthographic ? cam.orthographicSize : cam.fieldOfView;
    }

    private void Update()
    {
        HandlePinchInput();
        ApplyZoom();
    }

    private void HandlePinchInput()
    {
        if (Input.touchCount == 2)
        {
            Touch touch0 = Input.GetTouch(0);
            Touch touch1 = Input.GetTouch(1);

            float currentDistance = Vector2.Distance(touch0.position, touch1.position);

            if (!isPinching)
            {
                isPinching = true;
                previousTouchDistance = currentDistance;
                return;
            }

            float delta = previousTouchDistance - currentDistance;
            float zoomDelta = delta * zoomSpeed * Time.deltaTime;

            targetZoom += zoomDelta;
            targetZoom = Mathf.Clamp(targetZoom, minZoom, maxZoom);

            previousTouchDistance = currentDistance;
        }
        else
        {
            isPinching = false;
        }

        // Mouse scroll fallback for editor testing
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scroll) > 0.01f)
        {
            targetZoom -= scroll * zoomSpeed * 100f * Time.deltaTime;
            targetZoom = Mathf.Clamp(targetZoom, minZoom, maxZoom);
        }
    }

    private void ApplyZoom()
    {
        if (cam.orthographic)
        {
            if (enableSmoothing)
                cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, targetZoom, Time.deltaTime * smoothSpeed);
            else
                cam.orthographicSize = targetZoom;
        }
        else
        {
            if (enableSmoothing)
                cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetZoom, Time.deltaTime * smoothSpeed);
            else
                cam.fieldOfView = targetZoom;
        }
    }

    public void SetZoom(float zoom)
    {
        targetZoom = Mathf.Clamp(zoom, minZoom, maxZoom);
    }

    public void ResetZoom()
    {
        targetZoom = cam.orthographic ? 5f : 60f;
    }

    public float GetCurrentZoom()
    {
        return cam.orthographic ? cam.orthographicSize : cam.fieldOfView;
    }

    public float GetZoomNormalized()
    {
        float current = GetCurrentZoom();
        return Mathf.InverseLerp(minZoom, maxZoom, current);
    }
}
Ready for more? Gyroscope Camera Controller Controls camera rotation using the device gyroscope sensor with smoothing and sensitivity settings.