Contact Home

Restricting 2D Camera Zoom and Movement to an Image in Unity C#

Posted on: December 8th, 2015 by admin 8 Comments

The following code will allow you to constrain an orthographic 2D camera, so that it is always contained inside of a background image.

The script will calculate the movement constraints, as well as the max/min zoom values. Regardless of the aspect ratio, the camera will always stay within the image, therefore, never showing any bars on the top or sides of your scene.

The following image, is the demo running, while inside of Scene Mode, to give you an idea of how it works:

2D Camera Example

The source code is all contained in its own class. Read the comments for extra information:

using UnityEngine;

public class Camera2D : MonoBehaviour
{
    //The background image to use
    public Transform Area;      

    //Sprite details
    private Sprite Sprite;      
    private float PixelUnits;   
    private Vector2 Size;       
    private Vector2 Offset;

    //Camera bounds
    private float Left;
    private float Right;
    private float Top;
    private float Bottom;
    private float MaxZoom;
    private float MinZoom;

    public void Start()
    {
        Sprite = Area.transform.GetComponent<SpriteRenderer>().sprite;

        CalculatePixelUnits();
        CalculateSize();
        Refresh();
        Center();
    }

    //Calculate the pixel per unit value is for this sprite
    private void CalculatePixelUnits()
    {
        PixelUnits = Sprite.rect.width / Sprite.bounds.size.x;
    }

    //Calculate the size and offset of the background sprite
    private void CalculateSize()
    {
        Size = new Vector2(Area.transform.localScale.x * Sprite.texture.width / PixelUnits,
                            Area.transform.localScale.y * Sprite.texture.height / PixelUnits);

        Offset = Area.transform.position;
    }

    //Get zoom constraints, and zoom as large as possible for current view
    private void Refresh()
    {
        //calculate current screen ratio
        float w = Screen.width / Size.x;
        float h = Screen.height / Size.y;
        float ratio = w / h;
        float ratio2 = h / w;
        if (ratio2 > ratio)
        {
            MaxZoom = (Size.y / 2);
        }
        else
        {
            MaxZoom = (Size.y / 2);
            MaxZoom /= ratio;
        }

        MinZoom = 1;
        Camera.main.orthographicSize = MaxZoom;

        RefreshBounds();
    }

    //Position the camera at the center of the background image
    private void Center()
    {
        //set camera to center of background image
        Vector2 position = Area.transform.position;
        Vector3 camPosition = position;
        Vector3 point = Camera.main.WorldToViewportPoint(camPosition);
        Vector3 delta = camPosition - Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, point.z));
        Vector3 destination = transform.position + delta;
        transform.position = destination;
    }

    //Calculate the max distance the camera can travel
    private void RefreshBounds()
    {
        var vertExtent = Camera.main.orthographicSize;
        var horzExtent = vertExtent * Screen.width / Screen.height;

        Left = horzExtent - Size.x / 2.0f + Offset.x;
        Right = Size.x / 2.0f - horzExtent + Offset.x;
        Bottom = vertExtent - Size.y / 2.0f + Offset.y;
        Top = Size.y / 2.0f - vertExtent + Offset.y;
    }

    public void Update()
    {
        //You would typically hook into Refresh on a screen rotation or aspect ratio change
        //In demo, we call it non stop to demonstrate the camera system 
        Refresh();
    }

    public void LateUpdate()
    {
        //Clamp camera inside of our bounds
        Vector3 v3 = transform.position;
        v3.x = Mathf.Clamp(v3.x, Left, Right);
        v3.y = Mathf.Clamp(v3.y, Bottom, Top);
        transform.position = v3;
    }
    
    public void Zoom(float value)
    {
        Camera.main.orthographicSize += value;
        Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize, MinZoom, MaxZoom);
    }
}

The above script can be attached to a Orthographic Camera. The last thing you need to do is attach a GameObject that contains a SpriteRenderer to the newly added script:

Attach GameObject

You can also download a demo Unity project.

8 Responses

Ricky commented on April 30, 2016 at 8:23 pm

How would you change the code to work with a 3D object?

    admin commented on June 29, 2016 at 11:09 am

    That would be an entire new installment in the tutorial. I have considered doing this, however I am very busy with other projects at the moment. I’m hoping to address this at some point in time.

Abelius commented on August 26, 2016 at 10:32 pm

Thank you!! Works like a charm! I almost cried when I finally saw how it interacts with the standard 2D camera follow script. Perfect for side scrolling!

The only doubt I have… is it possible to implement a independent zoom in/out function without the script resetting the orthographic size value?

    admin commented on August 27, 2016 at 2:43 pm

    The script will automatically try to get the largest zoom possible. You could edit line 66 from above (Camera.main.orthographicSize = MaxZoom;) // so that when you refresh, you could set the zoom you want here. You will want to keep this value between MinZoom/MaxZoom.

Mark commented on October 7, 2016 at 11:29 pm

How would I change this code for a top down camera? I added zooming and dragging and WSAD control, but being new to Unity I am at a loss for making it work on X,Z as opposed to X,Y

Fabian commented on November 7, 2018 at 10:29 am

Thanks, your code save my life 😀

sam commented on September 4, 2019 at 6:34 am

Awesome thanks!

Respond

Leave a Reply to admin

You must be logged in to post a comment.