Unity 검색

Unity UI toolkit preview
Unity UI toolkit preview
공유

Is this article helpful for you?

Thank you for your feedback!

UI 툴킷은 사용자 인터페이스와 에디터 확장 프로그램을 개발하는 데 필요한 기능, 리소스, 툴 등의 모음집으로, 처음에는 UI Elements로 소개되었습니다. UI 툴킷에는 Unity 에디터 확장 프로그램에 따라 커스텀 UI를 손쉽게 개발할 수 있는 보류 모드 UI 프레임워크가 포함되어 있습니다.

UI 툴킷은 최신 버전의 Unity에서 사용할 수 있으며, 웹 기술에서 아이디어를 얻어 익숙하고 직관적인 저작 경험으로 런타임 UI 제작을 지원합니다. 이 글에서는 UI 툴킷에 어떤 기능이 있는지 살펴보고, UGUI 대신 UI 툴킷을 런타임에 처음 활용해 볼 때의 팁을 알아보겠습니다.

UI 시스템의 심층적인 비교 내용은 이 매뉴얼을 참조하세요.

Unity에서 시작하기

이제 Unity 2021 LTS를 다운로드하여 사용할 수 있습니다. 이번 릴리스에서는 1년이 넘는 시간 동안 전반적인 Unity 에디터와 기본 기능에 대한 집중적인 개발이 이뤄졌으며, Unity 2021.1 및 Unity 2021.2 테크 스트림 릴리스에서 추가된 기능을 6개월에 걸쳐 더욱 세부적으로 개선했습니다.

시작하려면 Unity Hub에서 Unity 최신 버전을 다운로드하세요.

Unity Hub login screen

워크플로

UI 툴킷을 사용해 UI 제작 워크플로를 더 원활하게 진행하는 방법을 알아보겠습니다. 

아티스트와 협업하여 UI를 만드는 작업은 복잡할 수 있습니다. 아티스트는 캔버스를 수정하며 컬러와 머티리얼을 추가하며, 그와 동시에 개발자는 스크립트, 동작과 OnClick 리스너를 추가합니다. 이후 이러한 요소를 병합하는 과정에서 충돌이 발생할 수 있고, 예상치 못한 긴급한 문제로 이어지기도 합니다.

UI 툴킷을 활용하면 아티스트는 UXML 및 USS 파일을 기반으로 작업하고, 로직 부문은 모두 C#으로 처리해 이러한 병합 충돌을 방지합니다. 예를 들어, 버튼 처리는 C#으로만 수행할 수 있습니다. 이 경우 버튼의 이름을 사용하여 버튼을 쿼리하며, UXML이나 USS 파일을 수정하지 않고 로직을 추가하게 됩니다.

이러한 프로세스로 병합이 더 간편해질 뿐 아니라 향후 스타일 변경도 간소화됩니다. 예를 들어 프로젝트의 모든 폰트를 갑자기 바꿔야 하는 경우, 텍스트 설정을 수정하기 위해 모든 에셋을 하나씩 변경하지 않아도 됩니다. 따라서 프로젝트의 규모가 클수록 더욱 복잡해지며 관리 문제로도 이어질 수 있는 지루한 작업을 피할 수 있습니다.

UI 툴킷의 경우, 패널 설정에 모든 텍스트 설정이 포함되어 있습니다. 따라서 UI Document의 폰트를 변경하려면 해당 패널 설정만 변경하면 됩니다. 에디터 스크립트도 UGUI로 비슷한 지원이 가능하나, UI 툴킷의 프레임워크는 이 프로세스를 자동으로 처리합니다.

Visual Element는 버튼, 이미지, 텍스트 등 모든 UI 툴킷 요소의 기본 클래스로, UI 툴킷의 게임 오브젝트라고 생각하면 됩니다. 또한 UI 빌더(Window > UI Toolkit > UI Builder)는 코드를 작성하지 않고도 인터페이스를 만들고 수정할 수 있는 시각적 툴입니다. UI 빌더는 개발 중인 UI를 시각화할 수 있으므로 아티스트와 디자이너 모두에게 유용합니다.

UI 툴킷은 웹 기술에 익숙한 사람들에게 적합하며, 로직과 스타일을 분리하여 더 세부적으로 관리하고 파일 충돌을 방지함으로써 아티스트와 개발자가 서로 더 원활하게 협업할 수 있게 해 줍니다. UI 빌더를 사용하여 요소의 위치, 스타일과 디자인을 설정하는 동시에 코드는 프로젝트 내 별도의 섹션에서 처리하여 코드에 필요한 UXML 부분을 쿼리하고 로직을 연결할 수 있습니다.

UXML 시작하기

UXML 파일은 HTML 웹 파일과 비슷하다고 볼 수 있는데, 이 파일에서는 시각적 요소를 나타내며 UI의 계층 구조도 포함하고 있습니다. Assets > Create > UI Toolkit > UI Document에서 UXML 문서를 만듭니다. 그런 다음 UI 빌더를 사용하여 새로 만든 UXML을 시각화하거나 관련 작업을 처리합니다.

여기서 주의할 점으로, UXML은 게임 오브젝트가 아닌 시각적 요소의 트리로 취급해야 합니다. 계층 구조를 간소화하려면 하나의 UI Document를 연결해 모든 UXML 요소를 로드하세요. Windows > UI Toolkit > Debugger에서 UI Toolkit Debugger로 UI를 시각화할 수 있습니다.

 

시작할 때 도움이 필요하면 Windows > UI Toolkit > Samples에 있는 몇 가지 UXML 요소 예제를 참조하세요.

UI 빌더를 사용하면 게임을 실행하지 않고도 UXML 파일 생성과 시각화, 테스트 등이 가능합니다. UI Builder Library 패널에서 UI 컨트롤(표준 또는 사용자 제작)을 계층 구조에 끌어다 놓고, 여러 개의 UXML 파일을 조합하여 최종 UI를 만들면 됩니다.

다음 세 개의 요소로 구성된 UI가 있다고 가정해 보겠습니다.

  • 체력
  • 점수
  • '승리' 팝업

승리 팝업에는 체력, 점수와 재시작 버튼이 표시됩니다.

각 요소는 health.uxml과 score.uxml 및 재시작 버튼이 모두 포함된 popup.uxml이 있는 별도의 UXML 파일의 일부이기 때문에, 게임의 서로 다른 UI 패널에서 사용됩니다. 따라서 점수와 체력을 관리하는 코드 로직(score.cs 및 health.cs)은 계층 구조와 독립되어 있습니다. 예를 들어 health.cs는 UI가 화면에 단독으로 표시되든 다른 팝업 안에 표시되든 항상 health.uxml 값을 업데이트합니다.

UXML을 조합해도 수정된 오브젝트를 시각화할 수 있습니다. UI 빌더 계층 구조 Class List 옵션을 활성화하면 오브젝트에 영향을 미치는 요인을 신속하게 파악하고 선택자를 사용하여 스타일을 관리할 수 있습니다.

씬(Scene) 계층 구조와 관련해서도 장점이 또 하나 있는데, UI 툴킷을 활용하면 하나의 캔버스 아래에 수십 개의 게임 오브젝트를 넣는 대신 UI Document에 하나의 UXML 파일만 연결하면 됩니다.

vs

이 워크플로는 특히 규모가 큰 팀에 유용합니다. 팀 단위 협업이 이뤄질 때 병합 충돌이 발생할 위험을 무릅쓰고 같은 씬에서 작업하는 대신, 각자의 UXML 파일에서 작업한 다음 해당 파일을 씬에 있는 하나의 UI Document에 추가할 수 있습니다.

패널 설정

패널 설정 에셋(Assets > Create > UI Toolkit > Panel Settings Asset)은 UXML이 게임 내에서 어떻게 인스턴스화되고 시각화될지 정의합니다. 여러 개의 패널 설정 에셋을 활용하여 UI마다 다른 스타일을 적용할 수 있습니다. HUD와 미니맵에 각기 다른 패널 설정을 적용하는 상황을 적절한 예로 들 수 있는데, 이 둘은 용도가 뚜렷이 구분되며 공통점이 거의 없는 UI이기 때문입니다.

패널 설정UGUI 캔버스와 캔버스 스케일러가 더해진 모양새에 패널 내 모든 텍스트가 동일한 기본 설정을 사용하도록 텍스트 설정과 같은 옵션을 추가한 것으로 간주할 수 있습니다. 이렇게 하면 각 폰트를 수동으로 재설정하는 시간을 줄일 수 있지만, 물론 필요한 경우 개별 UI Document에서 폰트를 오버라이드할 수도 있습니다.

이벤트

UI 빌더와 UXML 문서는 이벤트 관리 용도로 사용할 수 없습니다. 버튼 클릭이나 기타 이벤트를 처리하려면 C# 스크립트를 생성하여 UXML에 연결하세요.

public class UIEventHandler : MonoBehaviour
{
   [SerializeField]
   private UIDocument m_UIDocument;
 
   private Label m_Label;
   private int m_ButtonClickCount = 0;
   private Toggle m_Toggle;
   private Button m_Button;
 
   public void Start()
   {
       var rootElement = m_UIDocument.rootVisualElement;
 
       // This searches for the VisualElement Button named “EventButton”
       // rootElement.Query<> and rootElement.Q<> can both be used
       m_Button = rootElement.Q<Button>("EventButton");
 
       // Elements with no values like Buttons can register callBacks
// with clickable.clicked
       m_Button.clickable.clicked += OnButtonClicked;
 
       // This searches for the VisualElement Toggle named “ColorToggle”
       m_Toggle = rootElement.Query<Toggle>("ColorToggle");
 
       // Elements with changing values: toggle, TextField, etc... 
// implement the INotifyValueChanged interface,
       // meaning they use RegisterValueChangedCallback and 
// UnRegisterValueChangedCallback
       m_Toggle.RegisterValueChangedCallback(OnToggleValueChanged);
 
       // Cache the reference to the Label since we will access it repeatedly.
       // This avoids the relatively costly VisualElement search each time we update
       // the label.
       m_Label = rootElement.Q<Label>("IncrementLabel");
       m_Label.text = m_ButtonClickCount.ToString();
   }
 
   private void OnDestroy()
   {
       m_Button.clickable.clicked -= OnClicked;
       m_Toggle.UnregisterValueChangedCallback(OnToggleValueChanged);
   }
 
   private void OnButtonClicked()
   {
       m_ButtonClickCount++;
       m_Label.text = m_ButtonClickCount.ToString();
   }
 
   private void OnToggleValueChanged(ChangeEvent<bool> evt)
   {
       Debug.Log("New toggle value is: " + evt.newValue);
   }
}

위치 지정

요소의 위치 지정은 기본적으로 Flexbox 아키텍처를 사용합니다. 이를 통해 대다수의 레이아웃이 컨테이너나 화면 크기에 대응하도록 만들 수 있습니다. 두 개 이상의 요소가 있는 시각적 요소 트리에 적용한 다음, 요소가 트리에서 어떻게 정렬되는지 정의하세요.

요소의 위치 배정을 Absolute로 설정하면 부모 요소의 위치에 따라 해당 요소의 위치가 달라지며, 기타 다른 위치와 영향을 주고받지 않습니다.

FlexAlign 설정의 경우, 다음 간단한 계층 구조를 예제로 참고하세요.

UI 빌더 인스펙터를 사용하여 다음과 같은 설정을 수정합니다.

  • Basis: 축소(부모 요소에 남은 크기가 없는 경우, 축소율) 또는 확대(부모 요소에 남은 크기가 있는 경우, 확대 요소 비율)가 발생하기 전 항목의 기본 크기를 나타냅니다.
  • Direction(행과 열): 각각 UGUI의 Vertical Layout Group 및 Horizontal Layout Group과 비슷한 동작을 만들 수 있습니다.
Table Vertical Layout
  • Wrap: 맞지 않는 요소를 파악하여 무시하거나, 이전 요소의 위 또는 아래로 옮깁니다.
Table illustrating wrap
  • Align Items: 요소의 최소 크기를 사용하여 선택한 위치에 넣습니다.
Item alignment options
  • Justify Content: 주요 축을 따라 요소 사이의 간격을 정합니다.
Elements spacing options

시각적 요소의 위치 설정에 관한 자세한 내용은 UI 툴킷 레이아웃 엔진에 관한 기술 자료를 참조하세요.

스타일링

UI 툴킷은 스타일링에서 뛰어난 성능을 발휘합니다. 시각적 요소에 스타일을 추가하려면 USS 파일(Assets > Create > UI Toolkit > StyleSheet)을 사용합니다. Unity의 USS 파일은 웹 CSS 파일과 동등하며, 같은 규칙 기반 형식을 사용합니다.

사용자가 만든 USS 파일이 없어도 UI를 작동할 수 있습니다. 런타임 및 에디터 스타일은 기본적으로 제공되지만, 커스텀 USS를 만들면 이미 제공된 요소를 확장하거나 처음부터 원하는 스타일을 만들 수 있습니다.

UI 빌더 인스펙터의 StyleSheets 패널을 통해 스타일 선택자를 추가한 다음, 코드나 UI 빌더를 사용하여 수정할 수 있습니다.

UI 빌더의 USS 스타일 선택자

인스펙터에서 추가된 클래스 매칭

위 스크린샷에서는 버튼에 .RedButton 선택자가 추가되었습니다. 따라서 이 선택자는 모든 버튼에 기본적으로 포함되는 빌트인 .unity-text-element.unity-button 클래스와 함께 표시됩니다.

아래는 .RedButton 클래스가 있는 모든 요소의 배경 컬러를 빨간색으로 설정하는 USS 규칙의 예제입니다. 규칙의 첫 행은 클래스 이름을 사용하는 선택자이며, 적용할 스타일의 목록이 그 뒤로 이어집니다.

.RedButton { background-color: red; }

CSS와 마찬가지로 클래스 또한 여러 개를 함께 사용하여 규칙의 선택자 범위를 더 제한할 수 있습니다.

.RedButton.BlueText { color: blue; }

이 예제에서는 .RedButton 및 .BlueText 클래스가 모두 포함된 오브젝트만 텍스트가 파란색으로 설정됩니다. 이 예제를 이전 스니핏과 조합하면 버튼의 배경이 빨간색으로 설정됩니다.

USS 파일은 CSS 스타일과 마찬가지로 오브젝트의 상태를 기반으로 외관을 오버라이드하는 데 사용할 수 있습니다.

.unity-button:hover { background-color: red; }

이 예제에서는 마우스 커서를 올려놓은 모든 버튼의 배경이 빨간색으로 변합니다. 마우스 커서를 올렸을 때의 스타일링과 그 효과를 미리 보려면 UI 빌더 툴바의 Preview를 클릭하세요.

심화 예제

지금까지 UI 툴킷의 기본을 알아보았습니다. 이제 UGUI와 UI 툴킷을 사용하는 상황을 비교하며 샘플 인터페이스를 만들어 보겠습니다. 목표는 마우스를 올렸을 때 다음 두 가지의 효과가 나타나는 간단한 메뉴를 만드는 것입니다.

  • 버튼의 배경 컬러 변경
  • 버튼의 텍스트 컬러 변경

UGUI로 이 메뉴를 만들려면 계층 구조를 다음과 같이 설정합니다.

또한 일부 오브젝트에 몇 가지 컴포넌트를 추가해야 합니다.

  • Menu에는 배경 이미지가 있으며, 정렬을 위한 앵커링이 필요합니다.
  • Buttons는 적절하게 정렬할 수 있도록 올바르게 앵커링해야 합니다.
  • RestartQuit 버튼에 필요한 요소는 다음과 같습니다.
    • 마우스를 올리면 텍스트 컬러가 자동으로 변경되는 스크립트.
    • 배경 컬러를 변경하는 버튼 컴포넌트 설정(에디터 내에서 수정).

이 예에서는 Quit이 빨간색 버튼을 정의하는 프리팹입니다.

UI 빌더로 메뉴를 만드는 경우에는 비슷한 계층 구조로 시작합니다.

PopupAlignCenter로, ButtonsFlex DirectionRow로 설정하면 현재의 UI 툴킷 팝업이 다음과 같이 구현됩니다.

Quit 버튼은 QuitButton.uxml 컴포넌트를 사용해 빨간색으로 표시되며, 위에서 본 Quit 버튼 프리팹에 해당합니다.

PopupStyle.uss와 여러 개의 규칙을 추가하면 UI 툴킷의 진정한 성능을 확인할 수 있습니다.

.background {
   background-image: url('/Assets/Assets/OptionsMenu.png#OptionsMenu');
   width: 500px;
   height: 300px;
}
 
.title {
   font-size: 32px;
   color: rgb(255, 255, 255);
}
 
.unity-base-field__input {
   background-color: rgba(0, 0, 0, 0);
   background-image: url('/Assets/Assets/OptionsMenu9Slice.png#OptionsMenu9Slice_2');
   width: 300px;
   height: 75px;
   font-size: 20px;
   color: rgba(255, 255, 255, 0.5);
   -unity-text-align: middle-center;
}
 
.unity-button {
   color: white;
   background-color: rgba(0, 0, 0, 0);
   background-image: url('/Assets/Assets/StartMenu.png#StartMenu_ButtonDefault');
   width: 160px;
   height: 30px;
   -unity-slice-bottom: 1;
}
 
.unity-button:hover {
   color: rgb(0, 21, 255);
}
 
#Restart {
   -unity-background-image-tint-color: rgb(112, 202, 125);
}

여기서 선택자에 우선순위가 있다는 점에 유의하세요. 예를 들어 UXML에 직접 작성된 스타일은 USS 파일에 작성된 스타일을 오버라이드합니다. 여기서는 PopupStyle.uss에 요소의 너비와 높이를 추가했지만, 너비와 높이를 직접 UI 빌더에서 수정하고 PopupStyle.uss를 오버라이드할 수도 있습니다. 이 우선순위 규칙은 여기에서 확인할 수 있습니다.

메뉴의 컬러는 모두 변경하되 게임 나머지 부분의 컬러는 변경하지 않으려는 경우를 가정해 보겠습니다. 이 경우, UGUI를 사용하면 모든 컴포넌트의 컬러를 하나씩 수동으로 수정해야 합니다. 하지만 위의 예제를 생각해 보면, 여기서는 Quit 버튼만 프리팹에 해당하지만 모든 버튼을 프리팹으로 만들어 로컬에서 한 번에 오버라이드할 수 있습니다. 그런 다음 이후 수정 중에 발생하는 프리팹의 컬러 변경은 메뉴에서 무시됩니다.

UI 툴킷에서는 PopupStyle.uss 태그를 복사하여 NewStyle.uss를 만들고, PopupStyle.uss를 NewStyle.uss로 교체하기만 하면 됩니다.

씬에 UI 추가

UXML 파일을 생성하고 스타일링까지 마쳤다면, 마지막으로 이 파일을 실제 씬에 추가할 차례입니다. 씬 캔버스 요소를 교체하고 UI Document 요소가 있는 빈 게임 오브젝트를 만듭니다. UI Document를 popup.uxmlPanel Settings 파일로 채운 다음, 플레이 버튼을 눌러 새 UI를 테스트합니다.

UI 툴킷 사용해 보기

UI 툴킷을 사용해 보셨다면 UI 포럼에 의견을 보내거나, 공식 기술 자료에서 자세한 내용을 더 알아보세요.

마지막으로, 새로운 Unity 플랫폼 로드맵에서 최신 업데이트를 확인해 보세요. 제품 팀에 직접 피드백을 보내 주세요. 여러분의 의견을 기다리겠습니다.

이 글은 유능한 시니어 소프트웨어 개발자들로 구성된 유니티 Accelerate Solutions 팀의 마리나 조프리노가 작성했습니다. Accelerate Solutions 팀은 어떤 규모의 게임 스튜디오에나 컨설팅과 커스텀 개발 솔루션을 제공합니다. 웹사이트에서 Unity Accelerate Solutions 팀에 대해 알아보고, 여러분의 목표 달성에 어떤 도움이 될 수 있을지 살펴보세요.

2022년 4월 20일 엔진 & 플랫폼 | 20 분 소요

Is this article helpful for you?

Thank you for your feedback!