Sharing my scrollable/zoomable map script

I’ve been trying to find a good script for a drag-able/zoom-able world map, I haven’t found a good one so I decided to write one. Its done and I thought I post it here so I can save someone else from reinventing the wheel. Dragging and zooming function similar to that of Google map.

  • Attach to an orthagonal camera
using UnityEngine;
using System.Collections;

[RequireComponent (typeof(Camera))]
public class mapDragZoom : MonoBehaviour {
    public GameObject map; //map must have its center at (0,0,0) local position
    public float minSize = 4; //min max size of this orthogonal camer
    public float maxSize = 9;
    public float touchZoomSpeed = 0.01f; //multiplier for touch zoom to otho camera size

    public float inertiaDampingVal = 1.03f; //dividing factor for how quick inertia damp off

    private Camera cam;

    //drag vars
    private Vector3 dragStart;
    private float dragSSx, dragSSy;
    private bool wasDown = false;
    private bool nowDown;
    private Vector3 mapCenter;
    private Vector3 originalWS;

    private float mapHalfWidth, mapHalfHeight, camHalfWidth, camHalfHeight;

    private float minX, maxX, minZ, maxZ;
    private Vector3 newLocalPos;

    private Vector3 inertiaVector;


    void OnGUI() {
        GUI.TextArea (new Rect (10, 10, 150, 100),
            "minX" + minX+"\n" +
            "maxX" + maxX+"\n" +
            "minZ" + minZ+"\n" +
            "maxZ" + maxZ+"\n" +
            "newLocalPos" + newLocalPos.ToString()
        );

    }

    // Use this for initialization
    void Start () {
        inertiaVector = new Vector3(0,0,0);
        cam = GetComponent<Camera> ();
        mapCenter = map.transform.localPosition;
        newLocalPos = Vector3.zero;
    }
   
    // Update is called once per frame
    void Update () {
       
        processDrag ();
        processZoom ();

    }

    private void processDrag(){
        //damp inertia
        inertiaVector = inertiaVector / inertiaDampingVal;
        if (inertiaVector.magnitude < 0.005) {
            inertiaVector = new Vector3 (0,0,0);
        }

        //check if input is activated (mouse down or finger touching screen)
        if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) {
            nowDown = (Input.touchCount > 0);
        } else {
            nowDown = (Input.GetMouseButton (0) || Input.GetAxis ("Mouse ScrollWheel")!=0);
        }

        //calcualte how much camera need to change position
        Vector3 WSDiff = Vector3.zero;

        //if mouse currently holding or finger touching the screen
        if (nowDown) {
            //get screenspace input points
            //phone
            if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) {
                if (Input.touchCount == 1) {
                    //one finger
                    dragSSx = Input.GetTouch (0).position.x;
                    dragSSy = Input.GetTouch (0).position.y;
                } else if (Input.touchCount == 2) {
                    //check if player is switching between 1 finger to 2 finger
                    if (Input.GetTouch (0).phase == TouchPhase.Began || Input.GetTouch (0).phase == TouchPhase.Ended || Input.GetTouch (1).phase == TouchPhase.Began || Input.GetTouch (1).phase == TouchPhase.Ended) {
                        //if player add or remove finger, recalculate origional World Space point so that the map wouldn't jump around
                        wasDown = false;
                        return;
                    }
                    //two finger
                    dragSSx = (Input.GetTouch (0).position                    .x + Input.GetTouch (1).position.x)/2;
                    dragSSy = (Input.GetTouch (0).position.y + Input.GetTouch (1).position.y)/2;
                }
            } else {
                dragSSx = Input.mousePosition.x;
                dragSSy = Input.mousePosition.y;
            }

            //calcualte how much camera need to change position
            if (!wasDown) {
                originalWS = cam.ScreenToWorldPoint (new Vector3 (dragSSx, dragSSy, 1));
            } else if (wasDown) {
                Vector3 newWS = cam.ScreenToWorldPoint (new Vector3 (dragSSx, dragSSy, 1));
                WSDiff = (originalWS - newWS);
                inertiaVector = WSDiff;

            }
        }
        wasDown = nowDown;

        //move camera by WS diff vector or inertiaVector
        if (nowDown) {
            newLocalPos = transform.localPosition + WSDiff;
        } else {
            newLocalPos = transform.localPosition + inertiaVector;
        }

        //make sure camera does not go out of range
        mapHalfWidth = (map.GetComponent<MeshFilter> ().mesh.bounds.size.x * map.transform.localScale.x / 2);
        mapHalfHeight = (map.GetComponent<MeshFilter> ().mesh.bounds.size.z * map.transform.localScale.z / 2);
        camHalfHeight = 2f*cam.orthographicSize/2;
        camHalfWidth = camHalfHeight * cam.aspect;
        minX = mapCenter.x - mapHalfWidth + camHalfWidth; //min position camera should be at
        maxX = mapCenter.x + mapHalfWidth - camHalfWidth;
        minZ = mapCenter.z - mapHalfHeight + camHalfHeight;
        maxZ = mapCenter.z + mapHalfHeight - camHalfHeight;

        if (minX > maxX) { //incase the scale is too small, put camera at center
            newLocalPos = new Vector3(mapCenter.x, newLocalPos.y, newLocalPos.z);
        } else {
            if (newLocalPos.x - mapCenter.x < minX) {
                newLocalPos = new Vector3(minX, newLocalPos.y, newLocalPos.z);
            }
            if (newLocalPos.x - mapCenter.x > maxX) {
                newLocalPos = new Vector3(maxX, newLocalPos.y, newLocalPos.z);
            }
        }
        if (minZ > maxZ) { //incase the scale is too small, put camera at center
            newLocalPos = new Vector3(newLocalPos.x, newLocalPos.y, mapCenter.z);
        } else {
            if (newLocalPos.z - mapCenter.z < minZ) {
                Debug.Log ("MIN Z");
                newLocalPos = new Vector3(newLocalPos.x, newLocalPos.y, minZ);
            }
            if (newLocalPos.z - mapCenter.z > maxZ) {
                Debug.Log ("MAX Z");
                newLocalPos = new Vector3(newLocalPos.x, newLocalPos.y, maxZ);
            }
        }

        transform.localPosition = newLocalPos;
    }

    private void processZoom(){
        //Get increment values
        float increment = 0;
        //touch screen
        if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) {
            if (Input.touchCount == 2) {
                //two finger
                Touch touchZero = Input.GetTouch (0);
                Touch touchOne = Input.GetTouch (1);

                // Find the position in the previous frame of each touch.
                Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
                Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

                // Find the magnitude of the vector (the distance) between the touches in each frame.
                float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
                float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;

                // Find the difference in the distances between each frame.
                float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;

                //scale the zoom increment value base on touchZoomSpeed
                increment = deltaMagnitudeDiff * touchZoomSpeed;
            }
        } else {
            //mice and keyboard
            increment = Input.GetAxis ("Mouse ScrollWheel");
        }
        //calculate new orthographic camera size
        float currentSize = cam.orthographicSize;
        currentSize += increment;

        //check if size is out of the defined bound
        if (currentSize >= maxSize) {
            currentSize = maxSize;
        } else if (currentSize <= minSize) {
            currentSize = minSize;
        }

        //set new size
        cam.orthographicSize = currentSize;
    }
}
2 Likes

Thanks for sharing this!

Can you please provide the exact step how to make it work?

1 Like