UIElements는 Unity 2017.1에 도입된 실험적인 새 기능입니다.
이 문서를 읽으면서 예제 프로젝트를 살펴보는 것이 좋습니다.
프로젝트 예제는 https://github.com/Unity-Technologies/UIElementsExamples에서 찾을 수 있습니다.
면책 조항: *이 실험적인 기능은 아직 불완전하며 이후에 API가 변경될 수 있습니다. Unity는 여러분의 피드백을 환영하지만, UIElements는 아직 개발 단계에 있음을 기억해 주십시오.
또한 2017.2에 도입된 변경 사항은 2017.1로 다시 이식되지 않는다는 점도 알아두셨으면 합니다.*
지금까지 Unity 에디터 사용자 인터페이스는 대부분 즉시 모드 UI 시스템을 중심으로 제작되었습니다. IMGUI는 일부 상황에서 빛을 발하지만 에디터 기능 관련 작업을 수행하는 모든 작업자의 생산성에 영향을 미치는 심각한 설계적인 한계가 몇몇 있습니다.
이런 한계 때문에 보류 모드 UI 시스템인 UIElements 개발을 시작하게 되었으며, 결과적으로 성능을 향상하고 스타일시트, 동적/상황별 이벤트 처리, 접근성 등과 같은 여러 새로운 기능을 도입할 수 있게 되었습니다.
비주얼 트리(visual tree)는 “시각 요소”라는 가벼운 노드로 구성되어 있는 오브젝트 그래프입니다. 이 그래프를 이벤트 프로세싱, 레이아웃, 색칠하기 등에 사용할 수 있습니다. 노드는 C# 힙에 수동으로 할당되거나 에셋 로딩(지원 예정)을 통해 할당됩니다.
VisualElement
는 모든 비주얼 트리 노드의 공통 기본 클래스입니다. VisualElement
기본 클래스에는 스타일, 레이아웃 데이터, 로컬 변환, 이벤트 핸들러 등에 대한 속성이 있습니다.
VisualElement
에는 추가 동작 또는 기능을 정의하는 특수 컨트롤과 VisualContainer
를 포함한 몇몇 서브클래스가 있습니다. 하지만 서브클래스가 UIElements와 함께 작동하려면 클래스에서 파생되어야 한다는 엄격한 요구 사항은 없습니다. 내장 클래스에 필요한 기능이 대부분 포함되어 있기 때문입니다.
VisualContainer
오브젝트에는 VisualElement
자식 오브젝트가 있습니다. VisualContainer
클래스에는 그 자식 목록과 함께 작동하는 메서드가 몇 개 있습니다.
참고: UIElements 클래스는 현재 UnityEngine.Experimental.UIElements
네임스페이스 안에 있습니다.
새로 생성된 요소는 “패널”이라는 루트 오브젝트에 연결될 때까지 무시됩니다. 일반적으로 요소를 EditorWindow.rootVisualContainer 같은 기존 컨테이너 오브젝트에 추가하여 UI를 패널에 연결해야 합니다.
참고: UIElements가 실험 단계인 동안 이 프로퍼티를 실수로 사용하지 않도록 하기 위해 UnityEditor.Experimental.UIElements
네임스페이스에서 GetRootVisualContainer()
확장 메서드를 거쳐야 합니다.
VisualElement
가 루트에 연결되었는지 테스트하려면 이 요소의 panel
프로퍼티에 대해 테스트(연결되지 않은 경우 null
)를 수행해야 합니다.
트리의 요소는 다음과 같은 순서로 그려집니다.
부모가 자식보다 먼저 그려집니다.
형제는 부모의 자식 목록 순서대로 그려집니다.
그리기 순서를 변경하려면 부모의 VisualElement
오브젝트 순서를 변경해야 합니다.
특정 서브트리를 RenderTexture
에서 그린 이후에 VisualContainer.usePixelCaching
을 활성화하여 나중에 다시 그리기 이벤트에 픽셀을 재사용할 수 있습니다.
참고: VisualElement
를 서브클래스로 지정하고 DoRepaint() 메서드를 오버라이드하여 커스텀 그리기를 구현할 수 있지만, 이 방법은 향후 릴리스에서 변경될 예정이므로 권장되지 않습니다.
레이아웃 시스템은 VisualElement.position 프로퍼티(사각형 타입)에 영향을 미칩니다. 이 사각형은 부모의 좌표 공간을 기준으로 픽셀로 표시됩니다. 이 포지션을 첫 번째 스크립트 예와 같이 수동으로 할당할 수 있습니다.
각 VisualElement
에는 로컬 좌표 공간을 (포지션 사각형을 고려하기 전에) 부모 공간에서 변환하는 트랜스폼 프로퍼티(Matrix4x4
타입)도 있습니다.
마지막 VisualElement
창 공간 좌표는 VisualElement
의 전체 조상의 트랜스폼과 포지션을 고려하여 globalBound
프로퍼티를 통해 검색해서 가져올 수 있습니다.
참고: 2017.2에서는 transform
을 Unity 에디터의 Transform 컴포넌트와 유사하게 VisualElement의 Translate, Rotate, Scale 프로퍼티로 대체할 예정입니다.
VisualElementExtensions
정적 클래스에는 좌표 시스템 간 변환을 수행하는 확장 기능이 몇 개 있습니다.
VisualElement
및 VisualContainer
클래스에는 레이아웃을 제어하는 프로퍼티가 몇 개 있습니다. 기본적으로 모든 요소가 레이아웃의 일부이고, 기본 동작은 다음과 같습니다.
컨테이너는 자식을 수직으로 배포합니다.
컨테이너의 포지션 사각형은 자식의 사각형을 포괄합니다. (다른 레이아웃 프로퍼티에 따라 명시적으로 제한되는 경우 제외)
텍스트가 있는 VisualElement는 텍스트와 크기를 자동으로 적절하게 측정합니다. (다른 레이아웃 프로퍼티에 따라 명시적으로 제한되는 경우 제외)
기본 컨트롤도 레이아웃에 영향을 미치는 미리 정의된 스타일과 함께 제공됩니다.
UIElements의 레이아웃은 Flexbox라는 HTML/CSS 레이아웃 시스템의 부분 집합을 구현하는 Yoga 오픈 소스 프로젝트에 의존합니다.
Flexbox를 사용할 때 도움이 되는 몇몇 리소스는 다음과 같습니다.
Yoga의 자체 문서: 거의 모든 프로퍼티가 1 대 1로 일치됨
CSS-Tricks guide to Flexbox: 일부 사소한 차이가 있지만 대부분의 프로퍼티가 지원됨
주요 사항을 간략하게 확인하자면 다음과 같습니다.
대부분의 경우 width
와 height
를 간단히 요소에 할당하여 요소 크기를 지정할 수 있습니다.
형제 요소에 대한 요소의 상대적인 가중치 역할을 하는 flex
프로퍼티를 통해 유연한 크기를 지정할 수 있습니다.
flexDirection
프로퍼티를 행에 할당하여 가로 레이아웃으로 전환할 수 있습니다.
상대 위치 지정을 통해 요소의 원래 레이아웃 포지션에 따라 요소를 오프셋할 수 있습니다.
절대 위치 지정을 통해 요소의 부모 포지션 사각형을 기준으로 요소를 배치할 수 있습니다. 이 경우 요소가 형제 또는 부모에 영향을 미치지 않습니다.
“position” 프로퍼티가 API를 통해 직접 할당되는 요소는 자동으로 “absolute”로 간주됩니다.
요소가 교차 축에서 늘어나지 않게 하려면 FlexStart
를 각 VisualElement
자식 오브젝트의 alignSelf
프로퍼티나 부모의 alignItems
프로퍼티에 할당합니다.
VisualElement
에는 C#에서 직접 설정하거나 스타일시트에서 적용할 수 있는 스타일 프로퍼티가 있습니다. 레이아웃 프로퍼티 외에 요소가 화면에 그려지는 방법에 영향을 미치는 backgroundColor
또는 borderColor
같은 프로퍼티도 몇 개 있습니다.
참고: 2017.2에서는 스타일 기반 프로퍼티와 스타일 기반이 아닌 프로퍼티 간에 모호성을 없애기 위해 VisualElement
의 모든 스타일 프로퍼티가 별도의 데이터 구조로 추출됩니다.
Unity 에디터에서 스타일 시트는 USS 파일 포맷을 통해 지원됩니다. USS에 대한 자세한 내용은 Unity 스타일 시트 레퍼런스 문서를 참조하십시오.
Unity 에디터에서 스타일 시트를 성공적으로 생성한 후 AddStyleSheetPath()
메서드를 통해 스타일시트를 VisualContainer에 연결할 수 있습니다.
참고:
스타일 시트는 Assets 폴더의 “Resources” 또는 “Editor Default Resources” 폴더에 넣어야 합니다.
앞에서 언급한 메서드에 제공되는 경로는 둘러싸는 폴더에 대해 상대적입니다.
실행 중인 EditorWindow
에서 사용 중에 수정되는 스타일 시트는 즉시 적용되고, 스타일 시트에 따라 UI 스타일이 변경됩니다.
스타일 시트를 아무 VisualContainer
에나 연결할 수 있으므로 시트의 규칙이 이 컨테이너의 모든 자손에 적용됩니다.
입력 이벤트는 VisualElement
오브젝트로 직접 전파됩니다. 논리는 이벤트 타입 및 기타 요인에 따라 조금씩 다릅니다.
마우스 이벤트는 마우스 아래에 있는 요소에 전파됩니다.
키보드 이벤트는 포커스된 요소에 전파됩니다.
컨트롤은 이벤트를 “인식”하여 모든 이벤트를 직접 수신할 수 있습니다. (예를 들어 버튼을 마우스로 누를 때 이벤트를 인식하고, 마우스에서 손을 뗄 때 해당 이벤트가 해제됩니다.)
이벤트가 전파된다고 표현하는 이유는 (이벤트 인식 단계에서) 타겟 오브젝트의 조상이 후손보다 먼저 이벤트를 인식할 수도 있고, 중지되지 않았을 경우 (버블업 단계에서) 후손보다 나중에 이벤트를 인식할 수도 있기 때문입니다. HTML 또는 Flash 이벤트 모델에서 이런 패턴을 이미 보셨을 것입니다.
요소가 (TakeCapture()
확장 메서드를 통해) 이벤트를 인식할 때에는 해당 요소가 이벤트를 직접 수신하고 전파가 이루어지지 않습니다. 하지만 해당 요소가 이벤트를 사용하지 않는 경우에는 이벤트가 정상적으로 전파됩니다.
visible
하거나 enabled
되지 않은 요소는 이벤트를 수신하지 않습니다. 하지만 계층 구조에서 이벤트가 계속 전파됩니다.
다음 두 가지 방법으로 이벤트 핸들링 코드를 작성할 수 있습니다.
VisualElement
서브클래스에서 HandleEvent()
메서드를 오버라이드하는 방법
Manipulator를 서브클래스로 지정하고 서브클래스의 HandleEvent()
메서드를 오버라이드한 다음 VisualElement.AddManipulator()
를 추가하는 방법
이벤트 전파를 제어하는 방법에 따라 EventPropagation.Continue
또는 EventPropagation.Stop
을 반환해야 합니다. finalTarget
인수가 타겟 오브젝트로 설정됩니다.
Note: 2017.2에서는 IMGUI Event 클래스에서 벗어나 이벤트를 처리하기 위해 서브클래스를 작성해야 하는 필요성을 없앨 계획입니다. 그 대신 새 이벤트 타입을 콜백 기반 이벤트 등록 모델과 함께 도입할 계획입니다.
VisualElement
클래스에는 다음 두 값을 지원하는 pickingMode 프로퍼티가 있습니다.
PickingMode.Position
(기본값): 포지션 사각형을 기준으로 선별(picking)을 수행합니다.
PickingMode.Ignore
: 마우스 이벤트로 인한 선별(picking)을 방지합니다.
VisualElement.ContainsPoint()
메서드를 오버라이드하여 커스텀 교차 논리를 수행할 수 있습니다.
UIElements에는 다음과 같은 표준 컨트롤이 내장되어 있습니다.
Button
Toggle
Label
ScrollView
TextField
및 EditorTextField
컨트롤을 사용하는 예는 프로젝트 예제를 참조하십시오.
IMGUIContainer
클래스를 통해 IMGUI 코드를 VisualElement
안에서 사용할 수 있습니다. OnGUI() 안에서 일반적으로 수행할 수 있는 모든 작업이 지원됩니다. 여러 IMGUIContainer
를 배열하고 GUILayout
및 UIElements
레이아웃을 혼합하여 올바르게 배치할 수 있습니다.
하지만 UIElements를 IMGUIContainer
안에서 사용할 수는 없습니다.