
UI Toolkit: 커스텀 컨트롤 (Custom controls)
요약
Unity의 UI Toolkit을 사용하여 프로젝트에 특화된 재사용 가능한 커스텀 컨트롤을 제작하는 방법을 설명합니다. VisualElement를 상속받아 슬라이드 토글을 구현하고, UI Builder에서 사용할 수 있도록 Uxml 속성을 설정하는 과정을 다룹니다.
핵심 포인트
- VisualElement를 상속받아 커스텀 UI 요소 구축 가능
- Uxml 속성을 사용하여 UI Builder 및 인스펙터에 노출
- partial 클래스 및 public 접근 제어자 활용 필수
- 백킹 필드와 세터를 통한 UI 상태 동기화 구현
비디오: UI Toolkit: 커스텀 컨트롤 (Custom controls)
채널: Unity
길이: 12분 24초
출처: 자막 (수동, en-US)
UI Toolkit에는 많은 내장 컨트롤 (built-in controls)이 포함되어 있지만, 어느 시점에는 프로젝트에 특화된 무언가가 필요할 수 있습니다. 그럴 때 여러분은 직접 재사용 가능한 UI 요소 (UI elements)를 구축할 수 있습니다. 이 영상에서는 커스텀 VisualElement를 생성하고, 이를 UI Builder에 나타나게 하여 어디든 드래그 앤 드롭할 수 있도록 준비하는 과정을 살펴보겠습니다. 이번 데모에서는 간단한 커스텀 컨트롤인 슬라이드 토글 (slide toggle)을 만들어 보겠습니다. 이것은 불리언 (boolean) 값을 나타냅니다. 체크박스 (checkbox)와 비슷하지만, 레이블 (label)과 약간의 애니메이션 (animation)이 포함되어 있다고 생각하면 됩니다. 이것은 작고 재사용 가능한 UI 요소로, 커스텀 컨트롤로 만들기에 좋은 후보입니다.
이 컨트롤은 켜짐(on) 또는 꺼짐(off)이라는 단일 값을 가지며, 그 상태에 따라 나머지 모든 것이 업데이트됩니다. 레이블은 활성화(enabled) 및 비활성화(disabled) 텍스트 사이를 전환하고, 배경색 (background color)은 상태에 맞춰 변경되며, 노브 (knob)는 새로운 위치로 슬라이드합니다. 이제 컨트롤 자체를 만들어 보겠습니다. UI Toolkit에서 커스텀 컨트롤을 만들려면, 클래스가 VisualElement를 상속받아야 하며, UI Toolkit이 이를 Uxml에 노출하고 UI Builder에서 사용할 수 있도록 알려주는 Uxml element 속성 (attribute)을 사용해야 합니다. 또한 UI Builder가 이를 찾을 수 있도록 클래스는 public이어야 하며, Unity가 백그라운드에서 지원 코드를 생성하므로 partial이어야 합니다.
이렇게 하면 UI Builder의 커스텀 컨트롤 (custom controls) 항목 아래에 해당 컨트롤이 나타납니다. 지금 바로 레이아웃에 드래그하면 아직은 별다른 동작을 하지 않을 것입니다. 그저 빈 VisualElement일 뿐이지만, 이는 우리가 구축해 나갈 토대가 됩니다. UI Builder에서 구성할 수 있도록 몇 가지 속성 (properties)을 부여해 보겠습니다. 속성을 노출하려면 Uxml 속성을 사용하십시오. 이렇게 하면 요소가 선택되었을 때 인스펙터 (Inspector)에 나타납니다. 이 컨트롤의 경우, 텍스트의 활성화 및 비활성화 상태와 배경색에 대한 속성을 정의하겠습니다.
각 속성은 현재 값을 저장하기 위해 백킹 필드 (backing field)를 사용합니다. UI Builder에서 값을 변경하면, UI Builder가 세터 (setter)를 호출하며, 이를 통해 컨트롤이 인스펙터 (Inspector)에서 보이는 내용과 동기화된 상태를 유지합니다. 현재 이 속성들은 단순히 값만 저장할 뿐이며, 아직 우리의 UI 컨트롤에 영향을 미치지는 않습니다. 나중에 슬라이드 토글 (slide toggle) 상태에 따라 비주얼 (visuals)을 제어하는 데 사용할 것입니다. 현 시점에서 슬라이드 토글은 아직 스타일링이 되어 있지 않아 기본적으로 보이지 않는 상태입니다. UI Builder에서 제작하는 동안 실제로 볼 수 있도록 간단한 기본 색상을 부여하겠습니다.
이를 수행하기에 좋은 장소는 생성자 (constructor)입니다. 생성자는 요소 (element)가 생성될 때 실행되므로, 초기 값을 설정하는 곳으로 적합합니다. 지금은 요소가 보이도록 임시 배경색을 할당할 것이며, 잠시 후에 전체적인 비주얼 로직 (visual logic)으로 교체할 것입니다. 하지만 그전에, 우리 컨트롤에는 아직 상태 (state)가 없으므로 이를 해결해 보겠습니다. 지금까지 우리의 슬라이드 토글은 여전히 색상이 있는 VisualElement일 뿐이지만, 토글은 본질적으로 필드 (field)입니다. 토글은 켜짐(on) 또는 꺼짐(off) 중 하나의 값을 나타냅니다. 이 모든 동작을 직접 구현할 수도 있지만, 그럴 필요는 없습니다.
UI Toolkit에는 이미 값을 나타내는 컨트롤을 위한 베이스 클래스 (base class)가 있으며, 그 클래스는 BaseField입니다. BaseField는 실제로 VisualElement를 상속받습니다. 따라서 우리는 더 많은 내장 동작 (built-in behavior)을 가진 동일한 기본 UI 시스템을 그대로 사용하고 있는 것입니다. 또한 이는 제네릭 클래스 (generic class)입니다. 따라서 우리는 bool을 타입으로 사용할 것입니다. 이는 이 컨트롤이 불리언 (boolean) 값, 즉 True 또는 False, On 또는 Off를 저장한다는 것을 의미합니다. BaseField를 상속받음으로써 우리의 컨트롤은 많은 기능을 무료로 얻게 됩니다. 이미 value 속성, 내장된 변경 이벤트 (change events), 데이터 바인딩 (data binding) 지원, 그리고 레이블 (label)을 제공받습니다.
따라서 우리는 그 모든 것을 직접 다시 만들 필요가 없습니다. 그리고 이것은 컨트롤이 데이터를 나타낼 때 보통 적절한 기본 클래스 (base class)입니다. 만약 그렇지 않다면, 일반적인 VisualElement를 그대로 사용해도 됩니다. 한 가지 중요한 차이점이 있습니다. BaseField를 상속받으면 자체적인 생성자 (constructor)를 가집니다. 따라서 우리는 컨트롤에서 이를 호출해야 합니다. 이 생성자는 두 개의 매개변수, 즉 레이블 (label) 문자열과 입력 요소 (input element)를 받습니다. 레이블 문자열에는 null을 전달할 것인데, 이는 의도적으로 빈 상태로 시작하겠다는 의미입니다. 처음에는 어떤 레이블 텍스트도 보이지 않을 것입니다.
그다음, 입력 컨테이너 (input container) 역할을 할 새로운 VisualElement를 생성합니다. 그 부분이 필드 (field)를 나타내며, 우리는 여전히 매개변수가 없는 생성자를 유지합니다. 왜냐하면 UI Builder에서 요소를 생성할 때 Uxml이 기대하는 방식이 바로 그것이기 때문입니다. 따라서 우리는 단순히 몇 가지 기본값들을 기본 생성자 (base constructor)로 전달하고 있는 것입니다. 이를 통해 우리는 슬라이드 토글 (slide toggle)을 구축하는 데 사용할 깔끔한 구조를 얻게 됩니다. 이제 컨트롤의 내부 구조를 만들어 보겠습니다. 슬라이드 토글은 BaseField를 상속받기 때문에, 레이블과 입력 컨테이너를 포함한 몇 가지 내장된 UI를 실제로 함께 제공받습니다.
그러므로 모든 것을 처음부터 만드는 대신, BaseField가 이미 제공하는 것 위에 구축할 수 있습니다. 먼저, 몇 가지 USS 클래스 이름을 정의합니다. 우리는 이것들을 슬라이드 토글과 그 구성 요소들을 스타일링하기 위한 훅 (hooks)으로 사용할 것입니다. BEM 명명 규칙 (BEM naming)은 모든 것을 유지보수하기 더 쉽게 만들어 줍니다. 블록 (block)은 슬라이드 토글입니다. 요소 (element)는 입력 (input) 또는 입력 노브 (input knob)이며, 이 경우 입력은 체크된 (checked) 또는 켜짐 (on) 상태로 수정될 수 있습니다. 우리는 나중에 사용하기 위해 입력과 노브를 나타내는 요소들에 대한 참조 (references)를 저장해 둘 수 있습니다.
이제 다시 생성자 (constructor)로 돌아가서 구조를 다시 구축해 보겠습니다. 임시 색상을 지우고, 나중에 시각적 요소 (visuals)를 다시 추가하며, 루트 요소 (root element)에 우리의 커스텀 슬라이드 토글 (slide toggle) 클래스를 추가합니다. 다음으로 내장된 입력 컨테이너 (input container)를 가져옵니다. 그런 다음 나중에 스타일을 지정할 수 있도록 우리만의 클래스를 추가합니다. 이제 노브 (knob)를 생성합니다. 이것은 단지 자체 클래스를 가진 VisualElement일 뿐이며, 입력의 자식으로 추가할 수 있습니다. 이 시점에서 구조는 완성되었습니다. BaseField는 이미 라벨 (label) 요소, 입력 컨테이너, 그리고 값 (value)을 제공합니다.
우리는 토글을 시각적으로 나타내기 위해 노브라는 하나의 추가 요소를 더하고 있을 뿐입니다. 지금은 아직 아무런 동작도 하지 않지만, 실제 슬라이드 토글처럼 스타일을 지정하고 애니메이션을 적용하는 데 필요한 구조를 갖추었습니다. 이제 컨트롤을 상호작용 가능하게 만들어 보겠습니다. 컨트롤 전체를 클릭할 수 있도록 생성자에서 루트 요소에 클릭 콜백 (click callback)을 등록합니다. 사용자가 클릭하면, 우리는 단순히 값을 true에서 false로, 또는 그 반대로 뒤집습니다. 이것이 BaseField를 상속받는 주요 이점 중 하나입니다. 우리가 직접 상태 (state)를 관리하는 것이 아니라, 내장된 value 속성을 사용하는 것입니다.
이 값을 변경하면 UI Toolkit이 나머지 모든 작업을 처리합니다. 필드를 업데이트하고, 변경 이벤트 (change event)를 보내며, 데이터 바인딩 (data binding)을 사용 중이라면 이를 지원합니다. 우리는 클릭하는 행위가 먼저 상태를 변경하고, 그 후에 시각적 요소가 해당 상태에 반응하도록 만들고자 합니다. 시각적 요소를 업데이트하기 위해 UpdateVisuals라는 메서드를 추가하겠습니다. 이 메서드는 현재의 true/false 값을 받아 이를 올바른 색상, 텍스트 및 레이아웃으로 변환합니다. 또한 컨트롤이 올바른 시각적 상태로 시작할 수 있도록 생성자에서 이를 한 번 호출하겠습니다.
값이 켜져(on) 있을 때, 우리는 input 요소에 checked 클래스를 추가하며, 이 클래스가 애니메이션을 구동합니다. 이 클래스는 노브(knob)를 켜짐(on) 위치로 이동시킵니다. 또한 활성화된 상태를 반영하기 위해 배경색(background color)을 업데이트하며, base field가 이미 레이블(label)을 제공하므로 토글이 켜져 있는지 꺼져 있는지를 보여주도록 텍스트를 업데이트합니다. 즉, 값(value)은 상태를 나타내고, UpdateVisuals 메서드는 그 상태를 눈에 보이는 것으로 변환합니다. 이제 값이 변경될 때마다 이 작업이 실행되도록 보장해야 합니다. 이를 수행하기에 좋은 위치는 SetValueWithoutNotify라고 불리는 메서드입니다.
Base field는 이미 값을 할당하기 위해 이 메서드를 사용하고 있으므로, 우리는 이를 오버라이드(override)하여 기본 버전(base version)을 호출한 다음 시각적 요소(visuals)를 새로고침합니다. 이렇게 하면 값이 어떻게 변경되든 컨트롤이 동기화된 상태를 유지합니다. 클릭, 스크립트 로직, 또는 데이터 바인딩(data binding)을 통한 변경인지 여부는 중요하지 않습니다. 또한 속성(attributes)이 변경될 때도 값을 업데이트하고 싶을 것입니다. 현재 세터(setter)들은 단순히 값만 저장할 뿐 아직 UI에 영향을 주지 않습니다. 따라서 각 세터에 다음과 같이 UpdateVisuals 호출을 추가하겠습니다. 이제 UI Builder에서 또는 런타임(runtime)에서 텍스트나 색상이 변경될 때마다 컨트롤도 즉시 업데이트됩니다.
이제 USS에서 컨트롤의 스타일을 지정해 보겠습니다. 이 시점에서 우리는 이미 C#에서 USS 클래스 이름들을 할당했습니다. 이제 모든 것이 어떻게 보일지 정의해야 합니다. 아마 대부분의 작업은 UI Builder에서 대화형으로 구축하시겠지만, 작동하는 슬라이드 토글(slide toggle)을 위한 간단한 스타일시트(stylesheet)를 여기 준비했습니다. 거창한 것은 없으며, 단순히 input 트랙(track), 노브(knob), 그리고 체크 상태(check state)만 포함되어 있습니다. 먼저, 루트 요소(root element)가 사용 가능한 공간을 채웁니다. 다음으로 input이 트랙(track)이 됩니다. 여기에 크기를 지정하고 모서리를 둥글게 처리하며, 눈에 띄도록 테두리(border)를 추가합니다. 또한 색상 변경이 부드럽게 애니메이션되도록 트랜지션(transition)을 추가합니다.
그다음 knob(노브)를 위한 스타일 속성(style properties)을 정의합니다. 이것은 단순히 입력창(input) 내부에 위치하며 큰 테두리 반경(border radius)을 가진 원형 VisualElement입니다. check 클래스가 활성화되면, knob을 반대편으로 이동(translate)시킵니다. 트랜지션(transition)과 결합되어 이러한 슬라이딩 동작이 만들어집니다. 여기서 input의 배경색(background color)을 설정하지 않는다는 점에 주목하세요. 그 이유는 이미 C#에서 속성(attributes)을 사용하여 이를 제어하고 있기 때문입니다. 또한 base field가 자동으로 라벨(label)을 제공하므로 라벨의 스타일도 지정할 수 있습니다.
우리는 unity-label을 타겟팅하여 글꼴 크기(font size)와 정렬(alignment)을 조정하기만 하면 됩니다. 즉, C#이 상태(state)와 모든 동적 스타일링(dynamic styling)을 제어하고, USS가 레이아웃(layout)과 애니메이션(animation)을 처리합니다. 이제 어디든 가져다 놓을 수 있는 컨트롤(control)이 완성되었습니다. UI Builder에서 USS 스타일시트(stylesheet)를 Uxml에 추가합니다. 아직 하지 않았다면 레이아웃에 slide toggle을 배치하세요. 이를 선택하면 인스펙터(inspector)에 Uxml 속성(attribute)으로 노출한 속성들이 나타납니다. Base field에는 이미 라벨이 포함되어 있지만, 처음에는 비어 있는 상태로 시작하므로 즉시 보이지는 않을 것입니다.
라벨 필드에 무언가를 입력하여 보이게 만든 다음, text 및 color 속성을 조정하여 slide toggle을 커스터마이징하세요. 프리뷰 모드(preview mode)에 진입하면 런타임(runtime)에서 어떻게 보이는지 확인할 수 있습니다. 이것이 바로 커스텀 컨트롤(custom control)을 구축했을 때 얻는 진정한 보상입니다. 구조와 동작을 한 번 정의해 두면, 여러분이나 미래의 여러분이 필요할 때마다 언제든 재사용할 수 있습니다. 따라서 매번 동일한 토글(toggle)을 다시 만들 필요 없이, 이제 런타임의 어떤 UI에도 가져다 놓을 수 있는 재사용 가능한 컴포넌트(reusable component)를 갖게 된 것입니다. 다른 컨트롤과 마찬가지로, slide toggle의 동작을 제어하기 위해 여전히 C# 스크립트(script)를 사용하게 됩니다.
예를 들어, 슬라이드 토글(slide toggle)을 사용하여 효과음이나 음악을 음소거하고 싶다고 가정해 봅시다. 우리의 커스텀 슬라이드 토글을 사용하기 위해 다음과 같은 컴포넌트(component)를 만들 수 있습니다. 비주얼 트리(visual tree)에서 이를 쿼리(query)하고, 초기 값을 설정한 다음, BaseField를 상속받으므로 콜백(callback)을 등록합니다. 이미 값 변경 이벤트(value change events)를 지원하므로, 값이 변경될 때 여러분의 코드에서 이에 대응할 수 있습니다. 여기서는 단순히 결과를 로그(log)로 남기고 있지만, 이곳이 바로 오디오 시스템이나 다른 게임 로직(game logic)에 연결하는 지점입니다. 커스텀 컨트롤(custom controls)을 만드는 데 익숙해지면, 더 고급 UI를 다루기 시작할 수 있습니다.
QuizU 프로젝트에서는 동일한 접근 방식을 사용하여 원형 진행 바(radial progress bar)를 만드는데, 이는 원형 체력 바(health bar)나 속도계(speedometer) 같은 기능에 완벽합니다. 시각적으로는 더 복잡하지만, 구조는 본질적으로 동일합니다. 몇 가지 속성(attributes)과 프로퍼티(properties)를 정의합니다. C#에서 상태(state)를 업데이트한 다음, Painter2D의 벡터 API(vector API)와 결합된 USS가 시각적 요소를 처리하도록 합니다. 여러분의 애플리케이션만을 위해 설계된 독특한 컨트롤 라이브러리를 직접 구축하기 시작할 수 있습니다. 이 토글(toggle)과 같은 작은 것부터 시작하여 점차 확장해 나가세요.
더 자세히 알고 싶다면, 설명란의 링크를 확인하고 공식 UI Toolkit 문서(documentation)를 살펴보시기 바랍니다. 시청해 주셔서 감사합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 YouTube Unity 공식의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기