UI 비법서 (4) 

UI 개발자라면 제발 Rect Transform 애용합시다!

 

작성 기준 버전 :: 2019.1-2019.2

 

[이 포스트의 내용은 유튜브 영상으로도 시청하실 수 있습니다]

 

유니티 개발을 처음으로 공부하는 개발자들은 기본적으로 게임 오브젝트의 위치를 이동시키는 코드를 작성하려고 할 때, 제일 먼저 떠올리는 코드는 transform.position일 것이다. 

 

public class UIController : MonoBehaviour
{
    private void Update()
    {
        if(Input.GetMouseButtonDown(0) || Input.GetMouseButton(0))
        {
            transform.position = Input.mousePosition;
        }
    }
}

 

그래서 UI 게임 오브젝트를 이동시키는 코드를 작성하면 보통은 위의 예시 코드와 같이 작성하게 된다.

 

 

물론 에디터에서는 UI가 마우스 클릭을 따라서 잘 움직인다. transform.position으로 위치를 이동시키는 코드를 만들어도 UI 오브젝트를 잘 움직일 수는 있다.

 

 

Rect Transform

 

  

하지만 캔버스(Canvas) 밑에 속하는 UI 게임 오브젝트를 인스펙터 창에서 보면 트랜스폼(Transform) 대신에 렉트 트랜스폼(Rect Transform, 사각 트랜스폼)이 표시되는 것을 볼 수 있다. 유니티 엔진에서는 UI 오브젝트의 위치를 다룰 때, 트랜스폼 컴포넌트보다는 렉트 트랜스폼 컴포넌트를 사용하는 것을 권장한다는 의미로 볼 수 있다.

 

 

앵커(Anchor)

 

트랜스폼 컴포넌트와는 다르게 렉트 트랜스폼 컴포넌트는 앵커(Anchor)라고 하는 기준점을 가진다.

 

 

기본적으로는 앵커의 기준점이 middle, center로 설정되어 있는데, 이 기준점은 부모 UI 게임 오브젝트를 영역을 대상으로 한다.

 

 

이것을 씬 뷰에서 보면 제일 바깥의 하얀 사각형이 부모 UI 게임 오브젝트의 영역이며, 빨간색 이미지 중간에 4개의 삼각형이 짚고 있는 지점이 middle, center이다. 캔버스 바로 아래에 있는 UI 오브젝트의 부모 UI 영역은 일반적으로 스크린 전체를 의미한다.

 

 

노란 이미지의 자식 UI인 초록 이미지를 선택해보면 부모 UI 영역인 하얀 사각형이 노란 이미지를 크기만큼 지정되어 있고 앵커 역시 노란 이미지의 중심에 있는 것을 볼 수 있다. 즉, 자식 UI 오브젝트의 위치는 부모 UI 오브젝트의 영역을 대상으로 잡은 앵커를 중심으로 계산된다.

 

 

빨간색 십자선이 그려진 사각형 이미지를 선택하면 이 앵커의 위치를 정할 수 있는 몇 가지 프리셋을 보여주는데, 너비 앵커에는 left, center, right, stretch를 제공하고 높이 앵커에는 top, middle, bottom, stretch를 제공하며. 너비 앵커의 left, center, right나 높이 앵커의 top, middle, bottom 옵션은 부모 UI 영역의 왼쪽, 가운데, 오른쪽, 상단, 중단, 하단, 같은 특정한 위치를 의미한다. 그리고 너비 앵커와 높이 앵커 둘 다에 stretch 옵션이 있는데, 이것은 앞선 옵션들의 위치와는 조금 다른 의미를 가진다.

 

 

너비 앵커와 높이 앵커의 프리셋을 둘 다 stretch로 변경하면 원래는 Pos X, Pos Y, Width, Height이던 렉트 트랜스폼의 프로퍼티가 Left, Top, Right, Bottom으로 변경되는 것을 볼 수 있다.

 

 

각각의 프로퍼티는 부모 UI 영역의 경계선으로부터의 거리를 의미하며, 부모 UI의 영역의 너비와 높이의 변화에 영향을 받는다는 것이다.

 

 

단순한 예시로 부모 UI 영역의 너비가 늘어나면 위의 이미지처럼 Left와 Right의 간격을 유지하기 위해 빨간색 이미지 역시 늘어나게 된다. 하지만 노란색 이미지의 앵커는 middle, center로 부모 UI 영역의 너비와 높이로부터 영향을 받지 않기 때문에 여전히 정사각형 형태를 유지한다.

 

 

 

 

앵커의 간단한 활용

 

 

이러한 앵커의 활용법은 굉장히 간단하고도 유용하다. 만약 빨간 이미지의 UI를 항상 화면 왼쪽 상단에 위치 시키고 싶다고 가정해보자. 지금 해상도는 1920x1200으로 설정되어 있는데 해상도가 바뀐다면? 그리고 빨간 이미지의 앵커가 기본인 middle, center라면?

 

 

두 말할 것 없이 해상도가 바뀌는 순간 바로 이미지가 원하는 위치를 벗어나 버린다.

 

 

이 문제를 해결하기 위해서는 앵커를 top, left로 설정해주고 위치값을 적절하게 잡아주면 된다.

 

 

이렇게 해주면 어떤 해상도로 바뀌어도 빨간 이미지는 항상 화면 왼쪽 상단에 위치하게 된다.

 

 

두 번째 예시로는 화면 상단에 항상 저런 상단 바를 띄우고 싶을 때이다. 이런 상단 바는 모바일 게임에서 자주 사용되는 것으로 많은 개발자들이 알고 있듯이 모바일 기기는 신비하고 기괴한 해상도의 디바이스가 아주 많다. 그래서 반드시 모바일 디바이스별 해상도 대응에 신경을 써야한다.

 

 

이것 역시 UI의 앵커를 middle center로 두면 해상도가 바뀔 때마다 UI가 원하는 위치를 벗어나 버린다.

 

 

이때는 UI의 앵커를 top stretch로 설정해주고 위치를 잡아주면 된다.

 

 

이 다음에는 해상도가 변경되어도 상단바가 이상한 위치로 벗어나버리는 문제가 해결되는 것을 볼 수 있다.

 

 

 

 

스크립트에서의 렉트 트랜스폼

 

그럼 이제 다시 처음의 이야기로 돌아가보자. 일반적인 3D 공간에서의 게임 오브젝트의 위치를 이동시킬 때는 트랜스폼(Transform)을 이용한다. 그럼 UI 게임 오브젝트를 움직일 때는 무엇을 쓰면 좋겠는가? 그렇다! 렉트 트랜스폼(Rect Transform)이다!

 

using UnityEngine;

public class UIController : MonoBehaviour
{
    private RectTransform rectTransform;

    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0))
        {
            rectTransform.position = Input.mousePosition;
        }
    }
}

 

UI 오브젝트에 붙어서 UI 오브젝트의 위치를 수정할 스크립트에서는 렉트 트랜스폼을 이용해야 한다. 모노비헤이비어(MonoBehaviour)를 상속받은 게임 오브젝트에는 렉트 트랜스폼 멤버 변수가 없다. 그렇기 때문에 새로운 멤버 변수를 선언하고 GetComponent<RectTransform>()으로 자신이 가진 렉트 트랜스폼 컴포넌트를 가지고 와서 사용해야 한다.

 

 

위 코드를 작성한 뒤 저장하고 에디터에서 플레이해보면 이렇게 rectTransform.position을 사용해서 UI 오브젝트를 정상적으로 움직이는 것을 볼 수 있다.

 

using UnityEngine;

public class UIController : MonoBehaviour
{
    private RectTransform rectTransform;

    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0))
        {
            rectTransform.anchoredPosition = Input.mousePosition;
        }
    }
}

 

이 rectTransform.position 외에도 anchoredPosition을 사용해서도 UI의 위치를 옮길 수도 있다.

 

 

다만 이 경우에는 앵커의 위치에 영향을 받기 때문에 마우스의 위치를 따라가기를 원한다면 화면 좌측 하단 구석으로 앵커를 잡아주어야만 한다.

 

 

무엇이 문제인가?

 

사실 결과를 놓고 보면 transform.position을 이용해서 UI 오브젝트를 이동시킨 것과 rectTransfrom.postion을 이용해서 UI 오브젝트를 이동시킨 것이 결과가 똑같으니 뭘 쓰든지 상관없다고 여길 수도 있다.

 

사실 본인도 그런 생각이었지만, 이런 생각이 바뀐 계기가 있다. 전에 어떤 게임을 만든 적이 있는데, 그 때는 모든 UI 오브젝트를 transform.position 코드로 위치를 이동시켰다. 이것이 에디터, PC버전, 안드로이드 버전에서는 모두 정상적으로 동작했는데, 단 하나. iOS 버전 빌드에서 위치를 이동시키는 UI들이 정상적인 위치가 아닌 화면 좌측 하단 구석에 고정되는 문제가 발생했다. 당연히 이 문제로 며칠을 골머리를 앓았다. 그렇게 고통 받으며 문제를 해결하기 위해서 여러 가지 시도를 해보던 중에 rectTransform.position으로 UI 이동시키는 방법을 찾았고 그제서야 iOS 버전에서도 UI들이 정상적으로 움직이기 시작했다.

 

그러니 UI개발자라면 제발 렉트 트랜스폼을 애용하자. 이상한 문제를 만나기 전에...

반응형
  1. 2022.05.10 19:33

    진짜 좋은 글 감사합니당 :) 님 블로그와 유튜브 자주 찾게 되네요!

+ Recent posts