본문 바로가기

Unity

[Unity] 네이버 지도에서 줌 인/아웃 기능 구현하기

반응형

Unity를 사용한 지도 확대/축소 기능 구현하기

안녕하세요, 여러분! 오늘은 이전 포스트에 이어 PC에서는 에디터 테스트를 위한 기능을, Android에서는 실제 기능을 통해 네이버 지도를 확대/축소하는 방법을 알아보겠습니다. 이 기능은 두 플랫폼 모두에서 지도를 보다 직관적으로 탐색할 수 있게 해줍니다.

개요

우리는 PC에서는 마우스 휠을 이용한 에디터 테스트 기능을, Android에서는 터치 제스처를 이용한 실제 기능을 구현할 것입니다. 이를 위해 다음과 같은 단계를 거쳐 구현됩니다:

  1. Singleton 패턴을 이용한 ZoomController 클래스 생성
  2. PC 에디터 테스트용 마우스 휠 이벤트 처리
  3. Android 실제 기능용 터치 제스처 이벤트 처리
  4. 확대/축소 함수 작성
  5. 이벤트와 함수 연결
  6. NaverMapController 클래스에 기능 연결

ZoomController.cs

using UnityEngine;

public class ZoomController
{
    // ZoomController 인스턴스를 위한 정적 속성
    public static ZoomController Instance { get; private set; }
    // 이전 두 터치 포인트 간의 거리
    private float previousDistance;
    // 줌 중인지 여부를 나타내는 플래그
    private bool isZooming = false;
    // 줌 속도 상수
    private const float zoomSpeed = 0.1f;

    // 생성자를 private으로 설정하여 외부에서 인스턴스 생성을 막음
    private ZoomController() { }

    // ZoomController의 인스턴스를 초기화하는 메서드
    public static void Initialize()
    {
        // Instance가 아직 초기화되지 않은 경우
        if (Instance == null)
        {
            // 새 ZoomController 인스턴스를 생성하여 Instance에 할당
            Instance = new ZoomController();
        }
    }

    // 줌 동작을 처리하는 메서드
    public void HandleZoom()
    {
#if UNITY_EDITOR
        // 유니티 에디터에서 마우스 줌 동작을 처리
        HandleMouseZoom();
#endif

#if UNITY_ANDROID
        // 안드로이드에서 터치 줌 동작을 처리
        HandleTouchZoom();
#endif
    }

    // 마우스를 이용한 줌 동작을 처리하는 메서드
    private void HandleMouseZoom()
    {
        // 마우스 휠 입력을 받아옴
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        // 스크롤 값이 0이 아닌 경우
        if (scroll != 0.0f)
        {
            // NaverMapController의 줌 값을 업데이트하고 제한 범위 내로 클램프
            NaverMapController.Instance.zoom = Mathf.Clamp(NaverMapController.Instance.zoom + (scroll > 0 ? 1 : -1), 1, 20);
            // 업데이트된 줌 값으로 맵 타일을 다시 불러옴
            NaverMapController.Instance.StartCoroutine(NaverMapController.Instance.GetMapTile(NaverMapController.Instance.latitude, NaverMapController.Instance.longitude, NaverMapController.Instance.zoom));
        }
    }

    // 터치를 이용한 줌 동작을 처리하는 메서드
    private void HandleTouchZoom()
    {
        // 두 개의 터치 포인트가 감지된 경우
        if (Input.touchCount == 2)
        {
            // 첫 번째 터치 포인트
            Touch touch1 = Input.GetTouch(0);
            // 두 번째 터치 포인트
            Touch touch2 = Input.GetTouch(1);

            // 터치가 처음 시작된 경우
            if (touch1.phase == TouchPhase.Began || touch2.phase == TouchPhase.Began)
            {
                // 두 터치 포인트 간의 초기 거리를 저장
                previousDistance = Vector2.Distance(touch1.position, touch2.position);
                // 줌 중임을 표시
                isZooming = true;
            }
            // 터치 포인트가 이동한 경우
            else if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved)
            {
                // 현재 두 터치 포인트 간의 거리를 계산
                float currentDistance = Vector2.Distance(touch1.position, touch2.position);
                // 현재 거리와 이전 거리의 차이가 일정 값 이상인 경우
                if (Mathf.Abs(currentDistance - previousDistance) > 0.01f)
                {
                    // NaverMapController의 줌 값을 업데이트하고 제한 범위 내로 클램프
                    NaverMapController.Instance.zoom = Mathf.Clamp(NaverMapController.Instance.zoom + (currentDistance > previousDistance ? 1 : -1), 1, 20);
                    // 업데이트된 줌 값으로 맵 타일을 다시 불러옴
                    NaverMapController.Instance.StartCoroutine(NaverMapController.Instance.GetMapTile(NaverMapController.Instance.latitude, NaverMapController.Instance.longitude, NaverMapController.Instance.zoom));
                    // 이전 거리를 현재 거리로 업데이트
                    previousDistance = currentDistance;
                }
            }
            // 터치가 종료된 경우
            else if (touch1.phase == TouchPhase.Ended || touch2.phase == TouchPhase.Ended)
            {
                // 줌 중이 아님을 표시
                isZooming = false;
            }
        }
    }
}

이 코드는 유니티 게임 엔진에서 줌 동작을 처리하는 ZoomController 클래스입니다. 아래에 각 부분을 설명하겠습니다.

싱글톤 패턴

ZoomController 클래스는 Singleton 패턴을 사용하여 인스턴스를 하나만 생성할 수 있게 합니다. Instance 프로퍼티와 Initialize 메서드를 통해 인스턴스를 관리합니다.

public static ZoomController Instance { get; private set; }

private ZoomController() { }

public static void Initialize()
{
    if (Instance == null)
    {
        Instance = new ZoomController();
    }
}
  • Instance는 ZoomController의 유일한 인스턴스를 저장합니다.
  • private 생성자를 사용하여 외부에서 직접 인스턴스를 생성할 수 없게 합니다.
  • Initialize 메서드는 인스턴스가 없을 때만 새 인스턴스를 생성합니다.

줌 동작 처리

HandleZoom 메서드는 전처리기 지시문(UNITY_EDITOR 및 UNITY_ANDROID)을 사용하여 PC 에디터와 Android 환경에서 각각 적절한 확대/축소 이벤트를 처리하도록 합니다.

public void HandleZoom()
{
#if UNITY_EDITOR
    HandleMouseZoom();
#endif

#if UNITY_ANDROID
    HandleTouchZoom();
#endif
}

마우스 줌 처리

HandleMouseZoom 함수는 PC 에디터 테스트용으로 마우스 휠 이벤트를 처리하여 확대/축소를 수행합니다. Input.GetAxis("Mouse ScrollWheel")를 통해 마우스 휠의 스크롤 값을 가져오고, 이를 이용해 NaverMapController의 zoom 값을 조정합니다. Mathf.Clamp를 사용하여 확대/축소 범위를 제한합니다.

private void HandleMouseZoom()
{
    float scroll = Input.GetAxis("Mouse ScrollWheel");
    if (scroll != 0.0f)
    {
        NaverMapController.Instance.zoom = Mathf.Clamp(NaverMapController.Instance.zoom + (scroll > 0 ? 1 : -1), 1, 20);
        NaverMapController.Instance.StartCoroutine(NaverMapController.Instance.GetMapTile(NaverMapController.Instance.latitude, NaverMapController.Instance.longitude, NaverMapController.Instance.zoom));
    }
}
  • 마우스 휠 입력을 받아줌(scroll 값)을 감지합니다.
  • 줌 값을 업데이트하고 일정 범위(1~20) 내로 제한합니다.
  • 업데이트된 줌 값으로 맵 타일을 다시 불러옵니다.

터치 줌 처리

HandleTouchZoom 함수는 Android 실제 기능용으로 두 손가락 터치 제스처를 처리하여 확대/축소를 수행합니다. 두 손가락 터치의 이전 위치와 현재 위치 간의 거리를 계산하여 확대/축소 비율을 결정합니다. 이 값을 이용해 NaverMapController의 zoom 값을 조정하고, 마찬가지로 Mathf.Clamp를 사용하여 확대/축소 범위를 제한합니다.

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

        if (touch1.phase == TouchPhase.Began || touch2.phase == TouchPhase.Began)
        {
            previousDistance = Vector2.Distance(touch1.position, touch2.position);
            isZooming = true;
        }
        else if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved)
        {
            float currentDistance = Vector2.Distance(touch1.position, touch2.position);
            if (Mathf.Abs(currentDistance - previousDistance) > 0.01f)
            {
                NaverMapController.Instance.zoom = Mathf.Clamp(NaverMapController.Instance.zoom + (currentDistance > previousDistance ? 1 : -1), 1, 20);
                NaverMapController.Instance.StartCoroutine(NaverMapController.Instance.GetMapTile(NaverMapController.Instance.latitude, NaverMapController.Instance.longitude, NaverMapController.Instance.zoom));
                previousDistance = currentDistance;
            }
        }
        else if (touch1.phase == TouchPhase.Ended || touch2.phase == TouchPhase.Ended)
        {
            isZooming = false;
        }
    }
}
  • 두 손가락 터치를 감지합니다.
  • 터치가 시작되면 두 손가락 사이의 거리를 저장하고 줌 동작을 시작합니다.
  • 터치가 이동하면 현재 거리를 계산하고 이전 거리와 비교하여 줌 값을 업데이트합니다.
  • 터치가 종료되면 줌 동작을 종료합니다.

NaverMapController.cs

NaverMapController 클래스는 지도 이동 및 검색 기능을 포함하여 전체적인 맵 조작을 담당합니다. 따라서 ZoomController에서 함수화한 기능들을 본 스크립트에 연결하는 작업을 수행하겠습니다.

전체적인 코드는 같으며, 추가된 스니펫(코드 조각)을 기준으로 설명드리겠습니다.

NaverMapController 클래스에서 줌 동작 호출

// ...

void Update()
{
    ZoomController.Instance.HandleZoom();
}

// ...
  • Update 메서드에서 ZoomController.Instance.HandleZoom()을 호출하여 매 프레임마다 줌 입력을 처리합니다.

맵 타일을 요청하는 부분

// ...

public IEnumerator GetMapTile(float latitude, float longitude, int zoom)
{
    string apiUrl = $"{NaverMapAPI.Instance.mapStaticApiUrl}?w=500&h=500&center={longitude},{latitude}&level={zoom}&pos:{longitude} {latitude}";
    UnityWebRequest request = UnityWebRequestTexture.GetTexture(apiUrl);
    request.SetRequestHeader("X-NCP-APIGW-API-KEY-ID", NaverMapAPI.Instance.clientID);
    request.SetRequestHeader("X-NCP-APIGW-API-KEY", NaverMapAPI.Instance.clientSecret);
    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
    {
        Debug.LogError(request.error);
    }
    else
    {
        Texture2D texture = DownloadHandlerTexture.GetContent(request);
        mapImage.texture = texture;
        mapImage.SetNativeSize();
        mapImage.rectTransform.anchoredPosition = Vector2.zero;
        mapImage.transform.localScale = Vector3.one * 3;
    }
}

// ...
  • GetMapTile 코루틴: 새로운 맵 타일을 요청하여 받아오고, 이를 mapImage에 설정합니다. 이때, int zoom 매개변수가 추가되어 줌 레벨을 API 요청에 포함합니다. API URL 또한 줌 레벨을 포함하도록 변경되었습니다.

마무리

이번 포스트에서는 유니티에서 네이버 지도 API를 사용하여 PC와 Android 플랫폼 모두에서 직관적인 지도 확대/축소 기능을 구현하는 방법을 알아보았습니다. 싱글톤 패턴을 사용한 ZoomController 클래스 생성부터, PC에서는 마우스 휠 이벤트를, Android에서는 터치 제스처를 통해 각각의 줌 동작을 처리하는 방법을 상세히 설명했습니다. 또한, NaverMapController 클래스와의 연결을 통해 실시간으로 지도 타일을 갱신하는 기능을 구현하였습니다.

이 과정을 통해 여러분은 유니티에서의 네이버 지도 API 활용과 플랫폼별 입력 처리를 배우게 되셨을 것입니다. 유니티 프로젝트에서 이러한 기능을 적용함으로써 사용자 경험을 향상시키고, 다양한 환경에서 지도를 손쉽게 탐색할 수 있는 애플리케이션을 개발할 수 있게 됩니다. 앞으로도 유니티와 다양한 API를 활용한 프로젝트를 통해 더 많은 기능을 구현해보시길 바랍니다. 감사합니다.

반응형