Adventure Creator Wikia

This script lets you pan and zoom a 2D camera, confined to the boundary of the scene. It works both with the mouse, and on touch-screen devices.

To use it:

  1. Add a Camera to the scene, and attach AC's Basic Camera component to it.
  2. Assign this Camera as the scene's default, or use a Camera: Switch Action to switch to it
  3. Create a new C# script named PanZoomCamera, and copy/paste the code below.
  4. Attach the new Pan Zoom Camera component to your Camera, and fill in its Inspector.

PanZoomCamera.cs:

using UnityEngine;

public class PanZoomCamera : MonoBehaviour
{

	public float ZoomSpeedTouch = 0.02f;
	public float ZoomSpeedMouse = 0.5f;

	public SpriteRenderer backgroundConstraint = null;

	public float zoomMin = 1;
	public float zoomMax = 5.41f;
	public float zoomAcceleration = 7f;

	public float panAcceleration = 12f;

	private Camera cam;
	private float zoomSpeed;

	private Vector3 lastPanPosition;
	private Vector3 panOffset;

	private int panFingerId;
	private bool wasZoomingLastFrame;
	private Vector2[] lastZoomPositions;


	void Awake ()
	{
		cam = GetComponent<Camera> ();
	}

	void Start ()
	{
		ZoomCamera (0f, 0f, Vector2.zero);
	}


	void Update ()
	{
		if (Input.touchSupported)
		{
			HandleTouch ();
		}
		else
		{
			HandleMouse ();
		}

		transform.position = ClampPosition (transform.position);
	}


	void HandleTouch ()
	{
		float targetZoomSpeed = 0f;
		switch (Input.touchCount)
		{
			case 1:
			{
				wasZoomingLastFrame = false;

				Touch touch = Input.GetTouch(0);
				if (touch.phase == TouchPhase.Began) {
					lastPanPosition = touch.position;
					panFingerId = touch.fingerId;
				}
				else if (touch.fingerId == panFingerId && touch.phase == TouchPhase.Moved)
				{
					SetPanOffset (touch.position);
					lastPanPosition = touch.position;
				}
				else
				{
					panOffset = Vector3.Lerp (panOffset, Vector3.zero, Time.deltaTime * panAcceleration);
				}
				break;
			}

			case 2:
			{
				Touch touch0 = Input.GetTouch(0);
				Touch touch1 = Input.GetTouch(1);

				if (touch0.phase == TouchPhase.Ended || touch1.phase == TouchPhase.Ended)
				{
					if (touch0.phase != TouchPhase.Ended)
					{
						lastPanPosition = touch0.position;
					}
					else if (touch1.phase != TouchPhase.Ended)
					{
						lastPanPosition = touch1.position;
					}
					wasZoomingLastFrame = false;
				}
				else
				{
					Vector2[] newPositions = new Vector2[]{Input.GetTouch(0).position, Input.GetTouch(1).position};
					if (!wasZoomingLastFrame)
					{
						lastZoomPositions = newPositions;
						wasZoomingLastFrame = true;
					}
					else
					{
						float newDistance = Vector2.Distance(newPositions[0], newPositions[1]);
						float oldDistance = Vector2.Distance(lastZoomPositions[0], lastZoomPositions[1]);
						float offset = newDistance - oldDistance;

						targetZoomSpeed = offset;

						lastZoomPositions = newPositions;
					}
				}
				panOffset = Vector3.Lerp (panOffset, Vector3.zero, Time.deltaTime * panAcceleration);
				break;
			}

			default:
			{
				panOffset = Vector3.Lerp (panOffset, Vector3.zero, Time.deltaTime * panAcceleration);
				wasZoomingLastFrame = false;
				break;
			}
		}

		PanCamera ();

		zoomSpeed = Mathf.Lerp (zoomSpeed, targetZoomSpeed, zoomAcceleration * Time.deltaTime);

		if (lastZoomPositions != null && lastZoomPositions.Length == 2)
		{
			ZoomCamera(zoomSpeed, ZoomSpeedTouch, (lastZoomPositions[0] + lastZoomPositions[1]) * 0.5f);
		}
	}


	void HandleMouse ()
	{
		if (Input.GetMouseButtonDown (0))
		{
			lastPanPosition = Input.mousePosition;
		}
		else if (Input.GetMouseButton (0))
		{
			SetPanOffset (Input.mousePosition);
			lastPanPosition = Input.mousePosition;
		}
		else
		{
			panOffset = Vector3.Lerp (panOffset, Vector3.zero, Time.deltaTime * panAcceleration);
		}

		PanCamera ();

		float scroll = Input.GetAxis ("Mouse ScrollWheel");
		if (scroll != 0f)
		{
			zoomSpeed = scroll;
		}
		else
		{
			zoomSpeed = Mathf.Lerp (zoomSpeed, 0f, zoomAcceleration * Time.deltaTime);
		}

		ZoomCamera (zoomSpeed, ZoomSpeedMouse, Input.mousePosition);
	}


	void SetPanOffset (Vector3 newPanPosition)
	{
		Vector3 targetOffset = cam.ScreenToWorldPoint (new Vector3 (lastPanPosition.x, lastPanPosition.y, 10f)) - cam.ScreenToWorldPoint (new Vector3 (newPanPosition.x, newPanPosition.y, 10f));
		panOffset = Vector3.Lerp (panOffset, targetOffset, Time.deltaTime * panAcceleration);
	}


	void PanCamera ()
	{
		Vector3 move = new Vector3 (panOffset.x, panOffset.y, 0f);
		transform.Translate (move, Space.World);  
	}


	Vector3 ClampPosition (Vector3 position)
	{
		Vector3 bottomLeftWorldPosition = cam.ViewportToWorldPoint(new Vector3(0f, 0f, cam.nearClipPlane));
		Vector3 topRightWorldPosition = cam.ViewportToWorldPoint(new Vector3(1f, 1f, cam.nearClipPlane));

		Vector2 bottomLeftOffset = new Vector2(transform.position.x - bottomLeftWorldPosition.x, transform.position.y - bottomLeftWorldPosition.y);
		Vector2 topRightOffset = new Vector2(transform.position.x - topRightWorldPosition.x, transform.position.y - topRightWorldPosition.y);

		Vector2 hLimits = new Vector2(bottomLeftOffset.x + backgroundConstraint.bounds.min.x, topRightOffset.x + backgroundConstraint.bounds.max.x);
		float minX = hLimits.x;
		float maxX = hLimits.y;

		float scaleFactorX = (topRightWorldPosition.x - bottomLeftWorldPosition.x) / backgroundConstraint.bounds.size.x;
		if (scaleFactorX > 1f)
		{
			minX = maxX = backgroundConstraint.bounds.center.x;
			cam.orthographicSize /= scaleFactorX;
		}

		position.x = Mathf.Clamp (position.x, minX, maxX);

		Vector2 vLimits = new Vector2(bottomLeftOffset.y + backgroundConstraint.bounds.min.y, topRightOffset.y + backgroundConstraint.bounds.max.y);
		float minY = vLimits.x;
		float maxY = vLimits.y;

		float scaleFactorY = (topRightWorldPosition.y - bottomLeftWorldPosition.y) / backgroundConstraint.bounds.size.y;
		if (scaleFactorY > 1f)
		{
			minY = maxY = backgroundConstraint.bounds.center.y;
			cam.orthographicSize /= scaleFactorY;
		}

		position.y = Mathf.Clamp (position.y, minY, maxY);
		return position;
	}


	private void ZoomCamera (float offset, float speed, Vector2 screenPosition)
	{
		cam.orthographicSize = Mathf.Clamp(cam.orthographicSize - (offset * speed), zoomMin, zoomMax);
		if (cam.orthographicSize == zoomMin || cam.orthographicSize == zoomMax)
		{
			zoomSpeed = 0f;
		}

		if (offset > 0f)
		{
			Vector2 worldOffset = cam.ScreenToWorldPoint (screenPosition) - transform.position;
			transform.position = Vector3.Lerp (transform.position, transform.position + (Vector3) worldOffset, 0.1f * offset * Time.deltaTime);
		}
	}

}