Notice
Recent Posts
Archives
Today
Total
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Recent Comments
관리 메뉴

우당탕탕 개발일지

[Unity] 카메라 Drag 이동/줌 및 Scroll 줌 구현 본문

Unity

[Unity] 카메라 Drag 이동/줌 및 Scroll 줌 구현

devchop 2024. 3. 5. 16:22

1. 드래그를 할 경우(손가락 or 마우스) 카메라가 이동한다. Y축은 고정이고, x축과 z축만 이동한다. (3D기준)

2. 마우스 휠로 스크롤을 하거나 (PC) 두 손가락으로 확대/축소를 할경우 카메라가 줌/아웃된다.

3. 드래그와 줌 모두 최소/최대값이 존재한다.

 

이 코드는 외부에서 Init을 수행하고 min,max값을 설정하지 않으면 작동하지 않는다는점을 주의하자

이 코드의 카메라이동은 x와 z 축만 이동이 가능하고, Y축은 고정이라는 점을 주의하자. 만약 Y축을 이동하고싶다면 CheckMoveInput() 부분을 약간 수정하면된다.

 

더보기

전체코드

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraManager : SingletonMono<CameraManager>
{
    //movement
    private bool moveOn = false;
    private float moveSpeed = 10;
    private Vector2 moveBoundaryMin = Vector2.zero, moveBoundaryMax = Vector2.zero;
    private Vector3 moveStartPoint = Vector3.zero;

    //zoom
    private bool zoomOn = false;
    private float zoomMin = 0, zoomMax = 0, zoomSpeed = 10;

    private void Update()
    {
        bool isZoom = false;

        if (zoomOn)
        {
            isZoom = CheckDragZoomInput();
            if (!isZoom) isZoom = CheckScrollZoomInput();
        }

        if (!isZoom && moveOn) CheckMoveInput();
    }

    #region Init
    public void Init()
    {
        zoomOn = moveOn = false;
        transform.position = new Vector3(0, transform.position.y, 0);
    }
    public void SetMovement(bool isOn, Vector2 boundaryMin, Vector2 boundaryMax)
    {
        this.zoomOn = isOn;
        moveBoundaryMin = boundaryMin;
        moveBoundaryMax = boundaryMax;
    }

    public void SetZoom(bool isOn, float minSize = 0, float maxSize = 0)
    {
        moveOn = isOn;
        zoomMin = minSize;
        zoomMax = maxSize;
    }

    #endregion

    #region Zoom
    bool CheckScrollZoomInput()
    {
        float scrollValue = Input.GetAxis("Mouse ScrollWheel");
        if (scrollValue == 0) return false;


        var cam = Camera.main;
        float final = 0;

        if (cam.orthographic)
        {
            final = cam.orthographicSize + scrollValue * zoomSpeed;
            final = Mathf.Max(final, 0.1f);

            if (final < zoomMin) final = zoomMin;
            else if (final > zoomMax) final = zoomMax;

            cam.orthographicSize = final;
        }
        else
        {
            final = cam.fieldOfView + scrollValue * zoomSpeed;
            final = Mathf.Clamp(final, 0.1f, 179.9f);

            if (final < zoomMin) final = zoomMin;
            else if (final > zoomMax) final = zoomMax;

            cam.fieldOfView = final;
        }

        return true;
    }

    bool CheckDragZoomInput()
    {
        //drag zoom 
        if (Input.touchCount == 2)
        {
            Touch touchZero = Input.GetTouch(0);
            Touch touchOne = Input.GetTouch(1);

            Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
            Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

            float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
            float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;


            float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;

            var cam = Camera.main;
            float final = 0;
            if (cam.orthographic)
            {
                final = cam.orthographicSize + deltaMagnitudeDiff * zoomSpeed;
                final = Mathf.Max(final, 0.1f);

                if (final < zoomMin) final = zoomMin;
                else if (final > zoomMax) final = zoomMax;

                cam.orthographicSize = final;
            }
            else
            {
                final = cam.fieldOfView + deltaMagnitudeDiff * zoomSpeed;
                final = Mathf.Clamp(final, 0.1f, 179.9f);

                if (final < zoomMin) final = zoomMin;
                else if (final > zoomMax) final = zoomMax;

                cam.fieldOfView = final;
            }

            return true;
        }

        return false;
    }

    #endregion


    #region Move
    void CheckMoveInput()
    {
        if (Input.GetMouseButtonDown(0)) moveStartPoint = Input.mousePosition;
        if (Input.GetMouseButton(0))
        {
            var crrPos = Input.mousePosition;
            var pos = Camera.main.ScreenToViewportPoint(moveStartPoint - crrPos);
            pos.z = pos.y;
            pos.y = 0f;

            Vector3 finalPos = new Vector3(0, transform.position.y, 0);

            var move = pos.normalized * (Time.deltaTime * moveSpeed);
            transform.Translate(move);

            finalPos.x = transform.position.x;
            if (finalPos.x < moveBoundaryMin.x) finalPos.x = moveBoundaryMin.x;
            else if (finalPos.x > moveBoundaryMax.x) finalPos.x = moveBoundaryMax.x;

            finalPos.z = transform.position.z;
            if (finalPos.z < moveBoundaryMin.y) finalPos.z = moveBoundaryMin.y;
            else if (finalPos.z > moveBoundaryMax.y) finalPos.z = moveBoundaryMax.y;

            transform.position = finalPos;
        }
    }

    #endregion

}

 

 

 

 

코드분석

 

1. Manager 초기화 및 최소/최대값 세팅

public void Init()
    {
        zoomOn = moveOn = false;
        transform.position = new Vector3(0, transform.position.y, 0);
    }
    public void SetMovement(bool isOn, Vector2 boundaryMin, Vector2 boundaryMax)
    {
        this.zoomOn = isOn;
        moveBoundaryMin = boundaryMin;
        moveBoundaryMax = boundaryMax;
    }

    public void SetZoom(bool isOn, float minSize = 0, float maxSize = 0)
    {
        moveOn = isOn;
        zoomMin = minSize;
        zoomMax = maxSize;
    }

 

SetMovement : 이동기능의 경우 최소좌표와 최대좌표가 필요하다 

SetZoom: 확대/축소 기능의 경우 최소카메라 크기, 최대 카메라크기가 필요하다. 

GameManager에서 현재의 게임상황에 맞게끔 CameraManager를 초기화 및 초기설정을 해준다.

 

2. Update문

 private void Update()
    {
        bool isZoom = false;

        if (zoomOn)
        {
            isZoom = CheckDragZoomInput();
            if (!isZoom) isZoom = CheckScrollZoomInput();
        }

        if (!isZoom && moveOn) CheckMoveInput();
    }

 

줌/드래그 기능이 모두 한 곳에서 이루어지는데, 동작이 겹치지 않도록 하나의 작업이 성공적으로 끝날 경우 다른 작업은 시행되지 않도록 구현하였다.

zoomOn / moveOn 은 기능자체의 on/off 변수이다. 

 

 

3. CheckDragZoomInput()

if (Input.touchCount == 2)
        {
            Touch touchZero = Input.GetTouch(0);
            Touch touchOne = Input.GetTouch(1);

            Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
            Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

            float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
            float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;


            float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;

            var cam = Camera.main;
            float final = 0;
            if (cam.orthographic)
            {
                final = cam.orthographicSize + deltaMagnitudeDiff * zoomSpeed;
                final = Mathf.Max(final, 0.1f);

                if (final < zoomMin) final = zoomMin;
                else if (final > zoomMax) final = zoomMax;

                cam.orthographicSize = final;
            }
            else
            {
                final = cam.fieldOfView + deltaMagnitudeDiff * zoomSpeed;
                final = Mathf.Clamp(final, 0.1f, 179.9f);

                if (final < zoomMin) final = zoomMin;
                else if (final > zoomMax) final = zoomMax;

                cam.fieldOfView = final;
            }

            return true;
        }

        return false;

인풋을 조사하여 두손가락으로 zoom in /out 을 수행하는지 검사, 그러한 인풋이 들어왔다면 작업을 수행하고 true를 반환한다.

드래그는 손가락 두개이므로, input값이 총 2개이다. 초기값과 현재의 값(변화한 값) 을 계산하여 총 확대/축소를 얼마정도 했는지 수치로 변환한다(final) 

초기에 세팅한 zoomMax 와 zoomMin을 활용하여 최대/최소값을 넘지 않도록 세팅해준다.

 

4. CheckScrollZoomInput()

bool CheckScrollZoomInput()
    {
        float scrollValue = Input.GetAxis("Mouse ScrollWheel");
        if (scrollValue == 0) return false;


        var cam = Camera.main;
        float final = 0;

        if (cam.orthographic)
        {
            final = cam.orthographicSize + scrollValue * zoomSpeed;
            final = Mathf.Max(final, 0.1f);

            if (final < zoomMin) final = zoomMin;
            else if (final > zoomMax) final = zoomMax;

            cam.orthographicSize = final;
        }
        else
        {
            final = cam.fieldOfView + scrollValue * zoomSpeed;
            final = Mathf.Clamp(final, 0.1f, 179.9f);

            if (final < zoomMin) final = zoomMin;
            else if (final > zoomMax) final = zoomMax;

            cam.fieldOfView = final;
        }

        return true;
    }

 

얘는 간단한데, 스크롤 수치를 읽어서 확대/축소의 값에 사용한다. 나머지 부분은 (3)번과 동일하다.

 

5. CheckMoveInput()

void CheckMoveInput()
    {
        if (Input.GetMouseButtonDown(0)) moveStartPoint = Input.mousePosition;
        if (Input.GetMouseButton(0))
        {
            var crrPos = Input.mousePosition;
            var pos = Camera.main.ScreenToViewportPoint(moveStartPoint - crrPos);
            pos.z = pos.y;
            pos.y = 0f;

            Vector3 finalPos = new Vector3(0, transform.position.y, 0);

            var move = pos.normalized * (Time.deltaTime * moveSpeed);
            transform.Translate(move);

            finalPos.x = transform.position.x;
            if (finalPos.x < moveBoundaryMin.x) finalPos.x = moveBoundaryMin.x;
            else if (finalPos.x > moveBoundaryMax.x) finalPos.x = moveBoundaryMax.x;

            finalPos.z = transform.position.z;
            if (finalPos.z < moveBoundaryMin.y) finalPos.z = moveBoundaryMin.y;
            else if (finalPos.z > moveBoundaryMax.y) finalPos.z = moveBoundaryMax.y;

            transform.position = finalPos;
        }
    }

UI화면의 (x,y) 좌표의 이동값을 읽어서 카메라의 (x,0,z) 값으로 변환하여 이동하는 기능이다. 카메라는 3d 이기때문에 y값을 고정해야 의도한 대로 작동한다.  움직이기 전의 transform.position.y 값을 저장해놨다가 마지막에 y값에 넣어준다.

 

moveBoundaryMin, moveBoundaryMax 값 또한 UI좌표 기준으로 (x,y) 의 Vector2 형식을 띄고있으므로, 사실상 moveBoundary.y 는 카메라의 z의 바운더리를 나타낸다.