Fundamentals of Unity UI – 번역
Fundamentals of Unity UI
– 원문 링크 –
이전글 – A guide to optimizing Unity UI
Fundamentals of Unity UI
확인 완료한 버전: 5.3 – 난이도: 고급
유니티 UI 시스템을 구성하는 각 파트(부분)를 이해하는 것은 매우 중요합니다. 시스템을 구성하는 몇 가지 기본 클래스 및 컴포넌트가 있습니다. 이번 챕터에서는 먼저 이 글 전반에 걸쳐서 사용되는 여러 용어를 정의합니다. 그런 다음, 유니티 UI 주요 시스템의 로우 레벨(low-level) 동작에 대해서 살펴봅니다.
용어 (Terminology)
캔버스(Canvas)는 게임의 월드-공간의(World-Space) 위에(또는 월드-공간 내에) 그리는 지오메트리(Geometry, UI 메쉬)를 제공하기 위해서 유니티 렌더링 시스템이 사용하는 네이티브 코드(Native-Code) 유니티 컴포넌트입니다.
캔버스는 해당 캔버스를 구성하는 지오메트리(UI 메쉬)를 배치(Batch)로 결합하고 적절한 렌더링 명령(Render commands)을 생성한 다음 유니티의 그래픽 시스템에 전송합니다. 이 모든 작업은 네이티브 C++ 코드에서 수행되며, 리배치(Rebatch) 또는 배치 빌드(batch build)라고 부릅니다. 캔버스를 구성하는 지오메트리(UI 메쉬)가 리배치(rebatch)가 필요하다고 표시되면, 해당 캔버스는 dirty 상태로 간주됩니다.
지오메트리(UI 메쉬)는 Canvas Renderer 컴포넌트에 의해서 캔버스에 제공됩니다.
하위-캔버스(Sub-Canvas)는 단순히 다른 캔버스 컴포넌트 안에 중첩된(하위 계층으로 설정된) 캔버스 컴포넌트를 말합니다. 하위 캔버스(Sub-Canvas)는 이 캔버스 하위에 위치한 자식 UI 구성요소들을 부모 계층의 캔버스로부터 분리해서 따로 관리합니다; 즉, dirty 상태로 설정된 자식 UI 컴포넌트는 부모 계층의 캔버스를 구성하는 지오메트리(UI 메쉬)의 리빌드(rebuild)를 요청하지 않으며, 그 반대의 경우도 마찬가지 방식으로 동작합니다 (1).
Graphic은 유니티 UI C# 라이브러리에서 제공하는 기본 클래스입니다. 이 클래스는 모든 유니티 UI C# 클래스의 기본 클래스이며 화면에 그릴 수 있는(Drawable) 지오메트리(UI 메쉬)를 캔버스에 제공합니다. 유니티에 내장된 UI Graphic의 대부분은 MaskableGraphic 서브 클래스를 통해서 구현되어있기 때문에, IMaskable 인터페이스를 구현하면 마스크(mask)를 적용할 수 있습니다. Drawable 클래스의 주요 하위 클래스로는 Image 클래스와 Text 클래스가 있습니다. 이들은 이미지와 텍스트 기능을 제공합니다.
Layout 컴포넌트는 RectTransform의 크기 및 위치를 조절하며, Layout 컴포넌트에서 관리하는 컨텐츠(Content)의 상대적인 크기 또는 상대적인 위치 설정이 필요한 복잡한 레이아웃을 구성하는데 주로 사용됩니다. Layout 컴포넌트는 RectTransform에만 의존하며, RectTransform에 관련된 속성에만 영향을 줍니다. Layout 컴포넌트는 Graphic 클래스와 의존적인 관계가 아니기 때문에, 유니티 UI의 그래픽 컴포넌트와 독립적으로 사용할 수 있습니다.
Graphic과 Layout 컴포넌트는 모두 유니티 에디터 인터페이스에 노출되지 않은, CanvasUpdateRegistry 클래스에 의존적입니다. 이 클래스는 업데이트가 필요한 Layout 컴포넌트 및 Graphic 컴포넌트의 묶음(set)을 추적하고, 해당 캔버스에서 willRenderCanvases 이벤트를 발생시킬 때, 필요에 따라서 업데이트를 발생시킵니다.
Layout 및 Graphic 컴포넌트의 업데이트를 리빌드(Rebuild)라고 합니다. 리빌드 프로세스(Rebuild Process)는 이 글의 뒷부분에서 자세히 설명합니다.
렌더링 세부사항 (Rendering details)
유니티 UI에서 사용자 인터페이스를 구성할때는 캔버스에 의해서 화면에 그려지는 지오메트리(UI 메쉬)는 모두 Transparent 큐(Queue)에서 그려진다는 것을 명심해야 합니다. 즉, 유니티 UI에서 생성한 지오메트리(UI 메쉬)는 항상 알파 블렌딩으로, 뒤에서 앞의 순서로 화면에 그려집니다. 성능의 관점에서 기억해야하는 중요한 사항은, 폴리곤(polygon)으로부터 래스터화된(rasterized) 각 픽셀은 다른 폴리곤에 의해서 완전히 가려져 있더라도 샘플링된다는 점입니다. 모바일 장치에서 이렇게 높은 수준의 오버드로우(Overdraw)는 GPU에서 허용하는 fill-rate를 빠르게 초과할 수 있기 때문에 주의해야 합니다.
배치 생성 프로세스(캔버스) (The Batch building process (Canvases))
배치 생성 프로세스(The batch building process)는, 캔버스가 해당 UI 요소를 나타내는 메쉬를 결합하고 유니티의 그래픽 파이프 라인에 보낼 적절한 렌더링 명령을 생성하는 프로세스입니다. 이 프로셋의 결과는 캐시에 저장되고 캔버스가 dirty 상태로 표시되기 전까지 재사용 됩니다. 캔버스를 구성하는 메쉬 변화가 발생했을 때 캔버스가 dirty 상태로 표시됩니다.
캔버스에서 사용되는 메쉬는, 해당 캔버스에는 추가되어 있지만 하위 캔버스에는 포함되지 않은 Canvas Renderer 컴포넌트 묶음(세트)으로부터 가져옵니다. 배치(batches)를 계산하기 위해서는 깊이(depth, 뎊스)를 기준으로 메쉬를 정렬하고, overlap, 공유되는 재질(material)등을 검사해야합니다. 이 처리과정은 멀티-쓰레드로 동작하기 때문에 CPU 아키텍쳐에 따라서 성능 차이가 매우 큽니다. 특히, 모바일 SoC(일반적으로 CPU 코어의 수가 적은)와 최신 데스크탑 CPU(4개 이상의 코어를 가진)간의 성능 차이는 매우 큽니다.
리빌드 프로세스(그래픽) (The rebuild process(Graphics))
리빌드 프로세스는 레이아웃 및 유니티 UI의 C# Graphic 컴포넌트의 메쉬가 다시 계산되는 과정입니다. 이 처리과정은 CanvasUpdateRegistry 클래스에서 수행됩니다. 이 클래스는 C# 클래스로, Unity’s Bitbucket 에서 확인할 수 있습니다. CanvasUpdateRegistry 클래스에서 PerformUpdate 메소드를 주목해야 합니다. 이 메소드는 Canvas 컴포넌트에서 WillRenderCanvases 이벤트를 발생시킬때마다 호출됩니다. 또한 이 이벤트는 매 프레임마다 호출됩니다.
PerformUpdate 메소드는 아래와 같이 세단계의 프로세스를 실행합니다:
- ICanvasElement.Rebuild 메소드를 통해서 Dirty 상태로 표시된 Layout 컴포넌트의 리빌드를 요청합니다.
- 등록된 모든 클리핑 컴포넌트(마스크와 같이)에 대한 컬링(cull)처리를 요청합니다.
- Dirty 상태로 표시된 Graphic 컴포넌트의 그래픽 요소에 대한 리빌드를 요청합니다.
Layout과 Graphic의 리빌드 프로세스는 여러 부분으로 나뉘어 처리됩니다. Layout 리빌드는 3단계로 실행되며(PreLayout, Layout 및 PostLayout) Graphic 리빌드는 2단계로 실행됩니다.(PreRender and LatePreRender)
레이아웃 리빌드 (Layout rebuilds)
하나 이상의 Layout 컴포넌트에 포함되어있는 컴포넌트의 적절할 위치를(크기도 같이 계산될 수있음) 다시 계산하기 위해서는, 해당 Layout 컴포넌트에 계층 순서를 적절하게 설정해야 합니다. 게임오브젝트 계층의 루트에 가까운 레이아웃은, 해당 레이아웃과 중첩될 수 있는 레이아웃의 위치와 크기를 잠재적으로 변경할 수 있기 때문에, 가장 먼저 계산해야 합니다.
이를 위해서, 유니티 UI는 dirty 상태로 표시된 Layout 컴포넌트들의 목록을 계층 뷰의 순서대로 깊이(depth)를 설정해서 정렬합니다. 계층 뷰의 위에 있는(부모 트랜스폼의 수가 상대적으로 적은) 아이템들은 목록에서 앞에 위치합니다.
Layout 컴포넌트의 정렬을 마친 다음, 해당 레이아웃의 리빌드를 요청합니다; 이 과정에서 바로 Layout 컴포넌트에 의해서 조절되는 UI 요소의 위치 및 크기가 실제로 변경됩니다. 레이아웃에 의해서 영향을 받는 개별 요소(UI 요소)의 위치가 변경되는 방식에 대한 자세한 내용은 유니티 메뉴얼의 UI Auto Layout 섹션을 참고하시기 바랍니다.
그래픽 리빌드 (Graphic rebuilds)
Graphic 컴포넌트가 리빌드되면, 유니티 UI는 ICanvasElement 인터페이스의 Rebuild 메소드에 제어권을 전달합니다. Graphic 컴포넌트는 Rebuild 메소드를 구현하고(implement) Rebuild 프로세스의 PreRender 단계에서 두 가지의 다른 rebuild 단계를 실행합니다.
- 정점 데이터가 dirty 상태로 표시되면(예를들어, 컴포넌트의 RectTransform에서 크기를 변경한 경우), 메쉬가 리빌드 됩니다.
- 재질 데이터가 dirty 상태로 표시되면(예를들어, 컴포넌트의 재질 또는 텍스쳐가 변경된 경우), 해당 Canvas Renderer의 재질이 갱신(업데이트)됩니다.
그래픽 리빌드(Graphic Rebuilds)는 특정 순서대로 Graphic 컴포넌트의 목록을 처리하지 않기 때문에, 정렬작업이 필요하지 않습니다.
각주 (Endnotes)
1. 부모 Canvas를 변경하면 자식 Canvas의 크기가 변경되는 경우와 같이 이 동작 방식이 맞지 않는 특별한 경우도 있습니다.
내용 끝까지 읽어주셔서 감사합니다.
배너 클릭은 저에게 많은 힘이 됩니다.
감사합니다 🙂