티스토리 뷰
learn.unity.com/tutorial/optimizing-unity-ui?language=en#
Optimizing Unity UI - Unity Learn
Optimizing a user interface driven by Unity UI is an art. This guide will discuss the fundamental concepts, algorithms and code underlying Unity UI as well as discussing common problems and solutions.
learn.unity.com
1. Unity UI 최적화 가이드
Unity UI를 최적화 할 때의 핵심 긴장은 일괄 처리 비용과 드로 콜의 균형입니다. 일부 상식적인 기술을 사용하여 둘 중 하나를 줄일 수 있지만 복잡한 UI는 절충안을 만들어야합니다.
Unity UI를 최적화하려는 시도는 프로파일 링으로 시작해야합니다. Unity UI 시스템 최적화를 시도하기 전의 주요 작업은 관찰 된 성능 문제의 정확한 원인을 찾는 것입니다. Unity UI 사용자가 겪는 일반적인 문제는 네 가지입니다.
- 과도한 GPU fragment 셰이더 사용 (예 : fillrate 과다사용)
- Canvas batch를 재구축하는데 소요된 과도한 CPU 시간
- Canvas batch의 과도한 rebuild 횟수 (과도한 dirtying)
- vertex 생성에 과도한 CPU 시간 소비 (일반적으로 텍스트에서)
2. Unity UI의 기초
용어정의
캔버스는 유니티의 렌더링 시스템에서 사용하는 native코드로 된 유니티 컴포넌트, layered형태이고, worldspace의 top에 그려짐.
캔버스는 구성 지오메트리를 배치로 결합하여 적절한 렌더링 명령을 생성하고, 이를 Unity의 그래픽 시스템으로 보내는 역할을합니다. 이 모든 네이티브 C ++ 코드에서 수행되며,라고 rebatch 또는 일괄 빌드를 . 캔버스가 rebatch가 필요한 지오메트리를 포함하는 것으로 표시되면 캔버스는 dirty로 간주됩니다 .
캔버스에 지오메트리가 제공됩니다. (Canvas Renderer 구성 요소에 의해)
하위 캔버스는 단순히 또 다른 캔버스 요소 내에 중첩 된 캔버스 구성 요소입니다. 하위 캔버스는 자녀를 부모로부터 분리합니다. dirty로 표시된자식은 부모가 지오메트리를 다시 빌드하도록 강요하지 않으며 그 반대의 경우도 마찬가지입니다. 어떤경우에는 리빌드되는데. 예를 들어, 부모 Canvas에 대한 변경으로 인해 자식 Canvas의 크기가 조정되는 경우에 그렇습니다. (크기가 바뀌었으므로)
그래픽은 Unity UI C# 라이브러리에서 제공하는 기본 클래스입니다. 캔버스 시스템에 드로어블 지오메트리를 제공하는 모든 Unity UI C# 클래스의 기본 클래스입니다. 대부분의 내장 Unity UI 그래픽은 MaskableGraphic 서브 클래스를 통해 구현되며, IMaskable 인터페이스를 통해 마스킹 할 수 있습니다. Drawable의 주요 하위 클래스는 이미지와 텍스트로, 시조 구성 요소를 제공합니다.
레이아웃 컴포넌트는 RectTransforms의 크기와 위치를 제어하며 일반적으로 콘텐츠의 상대적 크기 또는 상대적 위치를 요구하는 복잡한 레이아웃을 만드는 데 사용됩니다. 레이아웃 구성 요소는 RectTransform에만 의존하며 관련 RectTransform의 속성에만 영향을줍니다. 그래픽 클래스에 종속되지 않으며 Unity UI의 그래픽 구성 요소와 독립적으로 사용할 수 있습니다.
그래픽 및 레이아웃 컴포넌트는 모두 Unity 편집기의 인터페이스에 표시되지 않는 CanvasUpdateRegistry 클래스에 의존합니다. 이 클래스는 업데이트해야하는 레이아웃 구성 요소 및 그래픽 구성 요소 집합을 추적하고 관련 Canvas가 willRenderCanvases 이벤트를 호출 할 때 필요에 따라 업데이트를 트리거합니다.
레이아웃 및 그래픽 구성 요소의 업데이트를 rebuild라고합니다. rebuild 프로세스는이 문서의 뒷부분에서 자세히 설명합니다.
렌더링 세부 정보
Unity UI에서 사용자 인터페이스를 작성할 때 Canvas에서 그린 모든 지오메트리는 Transparent 큐에 그려집니다. 즉, Unity UI에서 생성 된 지오메트리는 항상 알파 블렌딩을 사용하여 뒤에서 앞으로 그려집니다. 성능 관점에서 기억해야 할 중요한 점은 다각형에서 래스터화 된 각 픽셀이 다른 불투명 다각형으로 완전히 덮이더라도 샘플링된다는 것입니다. 모바일 장치에서이 높은 수준의 오버 드로우는 GPU의 fillrate 용량을 빠르게 초과 할 수 있습니다.
배치 구축 과정 (캔버스)
batch빌드 프로세스는 Canvas가 UI 요소를 나타내는 메시를 결합하고 Unity의 그래픽 파이프 라인으로 보낼 적절한 렌더링 명령을 생성하는 프로세스입니다. 이 프로세스의 결과는 Canvas가 더티로 표시 될 때까지 캐시되고 재사용됩니다. 이는 구성 메시 중 하나가 변경 될 때마다 발생합니다.
Canvas에서 사용하는 메시는 Canvas에 연결된 Canvas Renderer 컴포넌트 집합에서 가져 왔지만 하위 캔버스는 포함되어 있지 않습니다.
배치를 계산하려면 메시를 깊이별로 정렬하고 중첩(overlaps), shared material 등을 검사해야합니다. 이 작업은 다중 스레드이므로 성능은 일반적으로 다른 CPU 아키텍처에서, 특히 모바일 SoC (일반적으로 CPU 코어가 거의 없음)와 최신 데스크톱 CPU (종종 4 개 이상의 코어가 있음)간에 매우 다릅니다.
Rebuild 프로세스 (그래픽)
Rebuild 프로세스는 Unity UI의 C# 그래픽 컴포넌트의 레이아웃과 메시가 다시 계산되는 곳입니다. 이것은 CanvasUpdateRegistry 클래스 에서 수행됩니다 . 이것은 C# 클래스이며 해당 소스는 Unity의 Bitbucket 에서 찾을 수 있습니다 .
CanvasUpdateRegistry 내에서 관심있는 메서드는 PerformUpdate입니다. 이 메서드는 Canvas 컴포넌트가 WillRenderCanvases 이벤트를 호출 할 때마다 호출됩니다 . 이 이벤트는 프레임 당 한 번 호출됩니다.
PerformUpdate는 3 단계 프로세스를 실행합니다.
- 더티 레이아웃 구성 요소는 ICanvasElement.Rebuild 메서드 를 통해 레이아웃을 rebuild 하도록 요청됩니다 .
- 등록 된 클리핑 구성 요소 (예 : 마스크)는 클리핑된 구성 요소를 컬링하도록 요청됩니다. 이것은 ClippingRegistry.Cull을 통해 수행됩니다.
- 더티로 표시된 그래픽 컴포넌트는 rebuild하도록 요청됩니다.
레이아웃 및 그래픽 rebuild의 경우, 프로세스는 여러 부분으로 나뉩니다. 레이아웃 rebuild는 세 부분 (PreLayout, Layout 및 PostLayout)으로 실행되는 반면 그래픽 rebuild는 두 부분 (PreRender 및 LatePreRender)으로 실행됩니다.
레이아웃 Rebuild
하나 이상의 레이아웃 컴포넌트에 포함된 컴포넌트의 적절한 위치(및 잠재적 인 크기)를 다시 계산하려면 적절한 계층적 순서로 레이아웃을 적용해야합니다. GameObject 계층 구조에서 루트에 더 가까운 레이아웃은 잠재적으로 그 안에 중첩 될 수있는 레이아웃의 위치와 크기를 변경할 수 있으므로 먼저 계산해야합니다.
이를 위해 Unity UI는 더티 레이아웃 구성 요소 목록을 계층 구조의 깊이에 따라 정렬합니다. 계층 구조에서 더 높은 항목 (즉, 상위 변환이 더 적음)은 목록 맨 앞으로 이동됩니다.
그런 다음 레이아웃 구성 요소의 정렬 된 목록이 레이아웃을 다시 작성하도록 요청됩니다. 여기에서 Layout 구성 요소에 의해 제어되는 UI 요소의 위치와 크기가 실제로 변경됩니다. 개별 요소의 위치가 레이아웃에 의해 어떻게 영향을 받는지에 대한 자세한 내용은 Unity 매뉴얼의 UI 자동 레이아웃 섹션을 참조하십시오.
그래픽 Rebuild
그래픽 컴포넌트가 Rebuild되면 UnityUI는 ICanvasElement 인터페이스의 Rebuild 메서드에 제어를 전달합니다. Graphic은 이를 구현하고 Rebuild 프로세스의 PreRender 단계에서 두 가지 다른 다시 빌드 단계를 실행합니다.
- vertex 데이터가 더티로 표시된 경우 (예 : 컴포넌트의 RectTransform 크기가 변경된 경우) 메시가 다시 작성됩니다.
- material 데이터가 더티로 표시된 경우 (예 : 컴포넌트의 material 또는 텍스처가 변경된 경우) 첨부 된 Canvas Renderer의 재료가 업데이트됩니다.
그래픽 rebuild는 특정 순서로 그래픽 컴포넌트 목록으로 진행하지 않으며 정렬 작업이 필요하지 않습니다.
3. Unity UI 프로파일 링 도구
Unity UI의 성능을 분석하는 데 유용한 몇가지 프로파일링 도구가 있습니다. 주요 도구는 다음과 같습니다.
- Unity 프로파일러
- Unity 프레임 디버거
- Xcode의 기기 또는 Intel VTune
- Xcode의 프레임 디버거 또는 Intel GPA
외부 도구는 밀리 초 (또는 그 이상) 해상도의 메서드 수준 CPU 프로파일 링과 상세한 그리기 호출 및 셰이더 프로파일 링을 제공합니다. 위의 도구를 설정하고 사용하는 방법은이 가이드의 범위를 벗어납니다. XCode 프레임 디버거 및 기기는 Apple 플랫폼 용 IL2CPP 빌드에서만 사용할 수 있으므로 현재 iOS 빌드를 프로파일 링하는 데만 사용할 수 있습니다.
Unity Profiler
Unity 프로파일러의 주요용도는 비교 프로파일링을 수행하는 것입니다. Unity 프로파일러가 실행되는 동안 UI의 요소를 활성화 및 비활성화하면 성능 문제를 가장 많이 담당하는 UI 계층 부분을 빠르게 좁힐 수 있습니다.
이를 분석하려면 프로파일 러의 출력에서 Canvas.BuildBatch 및 Canvas.SendWillRenderCanvases 줄을 확인하십시오.
Canvas.BuildBatch 는 앞서 설명한대로 CanvasBatchBuilding 프로세스를 수행하는 네이티브코드 계산입니다.
Canvas.SendWillRenderCanvases 에는 Canvas 구성 요소의 willRenderCanvases 이벤트를 구독하는 C# 스크립트의 호출이 포함됩니다 . Unity UI의 CanvasUpdateRegistry 클래스는이 이벤트를 수신하고 이를 사용하여 Rebuild 프로세스를 실행합니다. 현재 더티 UI 구성 요소는 캔버스 렌더러를 업데이트 할 것으로 예상됩니다.
참고 : UI 성능의 차이를보다 쉽게 확인하려면 일반적으로 "렌더링", "스크립트"및 "UI"를 제외한 모든 추적 범주를 비활성화하는 것이 좋습니다. CPU 사용량 프로파일러의 왼쪽에있는 추적 범주 이름 옆에있는 색상 상자를 클릭하면됩니다. 범주 이름을 클릭하고 위쪽 또는 아래쪽으로 끌어 CPU 프로파일러에서 범주를 다시 정렬 할 수도 있습니다.
UI 범주는 Unity 2017.1 이상에서 새로 추가되었습니다. 안타깝게도 UI 업데이트 프로세스의 일부가 올바르게 분류되지 않았으므로 모든 UI 관련 호출이 포함되지 않을 수 있으므로 UI 곡선을 볼 때주의해야합니다. 예를 들어 Canvas.SendWillRenderCanvases 는 "UI" 로 분류 되지만 Canvas.BuildBatch 는 "Others"및 "Rendering"으로 분류됩니다.
2017.1 이상에는 새로운 UI 프로파일러도 있습니다. 기본적으로 이 프로파일러는 프로파일러 창의 마지막 프로파일러입니다. 두 개의 타임 라인과 일괄 뷰어로 구성됩니다.
첫 번째 타임 라인은 각각 레이아웃과 렌더링을 계산하는 두 범주에서 소비 된 CPU시간을 보여줍니다. 이전에 설명한 것과 동일한 문제가 발생하며 일부 UI 기능이 고려되지 않을 수 있습니다.
두 번째 타임 라인은 batch, vertex의 총 갯수를 표시하고 이벤트 마커도 표시합니다. 이전 스크린 샷에서 몇 가지 버튼 클릭 이벤트를 볼 수 있습니다. 이러한 마커는 CPU 스파이크의 원인을 파악하는 데 도움이 될 수 있습니다.
마지막으로 UI 프로파일러의 가장 유용한 기능은 하단의 batch 뷰어입니다. 왼쪽에는 모든 캔버스의 트리보기와 각 캔버스 아래에 생성 된 배치 목록이 있습니다. 열은 각 캔버스 또는 배치에 대한 흥미로운 세부 정보를 제공하지만 UI를 최적화하는 방법을 더 잘 이해하는 데 특히 중요한 것이 있으며 batch중단 이유 입니다. 이 열에는 선택한 배치를 이전 배치와 병합 할 수없는 이유가 표시됩니다. 일괄 처리 수를 줄이는 것은 UI 성능을 개선하는 가장 효과적인 방법 중 하나이므로 batch처리를 중단하는 요소를 이해하는 것이 중요합니다.
스크린샷에서 볼 수 있듯이 가장 빈번한 이유 중 하나는 다른 텍스처 또는 재질을 사용하는 UI 요소입니다. 대부분의 경우 스프라이트 아틀라스 를 사용하여 쉽게 수정할 수 있습니다 . 마지막 열에는 배치와 관련된 게임 개체의 이름이 표시됩니다. 이름을 두 번 클릭하여 편집기에서 게임 오브젝트를 선택할 수 있습니다 (이는 같은 이름의 오브젝트가 여러 개있을 때 특히 유용합니다).
Unity 2017.3부터 배치 뷰어는 Editor에서만 작동합니다 . 일반적으로 batch처리는 기기에서 동일해야하므로 여전히 유용합니다. 장치에서 배치가 다를 수 있는지 의심스러운 경우 다음에 설명할 프레임 디버거를 사용할 수 있습니다.
Unity 프레임 디버거
Unity 프레임 디버거는 Unity UI에서 생성되는 드로콜수를 줄이는 데 유용한 도구입니다. 이 내장 도구는 Unity 에디터의 창 메뉴를 통해 액세스 할 수 있습니다. 활성화되면 Unity UI에서 생성 된 것을 포함하여 Unity에서 생성 된 모든 드로 콜을 표시합니다.
특히, 프레임 디버거는 Unity 에디터에서 게임 뷰를 표시하기 위해 생성 된 드로콜로 자체 업데이트되므로 플레이 모드에 들어 가지 않고도 다른 UI 구성을 시도하는 데 사용할 수 있습니다.
Unity UI 드로콜의 위치는 그려지는 Canvas 컴포넌트에서 선택한 렌더모드에 따라 다릅니다.
- Screen Space – Canvas.RenderOverlays 그룹 내에 오버레이 가 나타납니다.
- Screen Space – Camera가 Render.TransparentGeometry의 하위 그룹으로 선택된 Render Camera의 Camera.Render 그룹 내에 나타납니다.
- World Space 는 Canvas가 보이는 각 World Space 카메라에 대해 Render.TransparentGeometry의 하위 그룹으로 나타납니다.
모든 UI는 "Shader : UI / Default"줄로 식별 할 수 있습니다 (UI 셰이더가 사용자 지정 셰이더로 대체되지 않았다고 가정). 그룹 또는 드로우 콜의 세부 사항. 아래 스크린 샷에서 강조 표시된 빨간색 상자를 참조하십시오.
UI를 조정하는 동안이 라인세트를 관찰하면 UI 요소를 배치로 결합하는 Canvas의 기능을 최대화하는것이 상대적으로 쉽습니다. 배치가 중단되는 가장 일반적인 설계 관련 원인은 의도하지 않은 중복입니다.
모든 Unity UI 컴포넌트는 지오메트리를 일련의 쿼드로 생성합니다. 그러나 많은 UI 스프라이트 또는 UI 텍스트 glyph는 이를 나타내는 데 사용되는 쿼드 중 일부만 차지하고 나머지는 빈공간입니다. 결과적으로 UI 디자이너가 의도하지 않게 텍스처가 서로 다른 재질에서 가져 와서 일괄 처리 할 수 없는 여러 개의 서로 다른 쿼드를 겹친다는 것을 발견하는 문제는 빈번합니다.
Unity UI는 완전히 transparent queue에서 작동하므로 그 위에 batch할 수없는 쿼드가 중첩 된 쿼드는 batch할 수없는 쿼드보다 먼저 그려야하므로 batch할 수 없는 쿼드 위에 batch된 다른 쿼드와 함께 batch할 수 없습니다.
3 개의 쿼드 A, B, C의 경우를 생각해보십시오. 3 개의 쿼드가 모두 서로 겹치고, 쿼드 A와 C는 동일한 재질을 사용하고 쿼드 B는 별도의 재질을 사용한다고 가정합니다. 따라서 쿼드 B는 A 또는 C로 일괄 처리 할 수 없습니다.
계층 구조의 순서 (위에서 아래로)가 A, B, C 인 경우 B는 A와 C 아래에 그려야하기 때문에 A와 C는 일괄처리 할 수 없습니다. 그러나 B가 일괄처리 가능한 쿼드의 앞이나 뒤에 배치되면 그러면 batchable쿼드가 실제로 배치될 수 있습니다. B는 batch된 쿼드 앞이나 뒤에 만 그려야하며 batch되지 않습니다.
Instruments & VTune
Xcode의 Instruments와 Intel의 VTune은 각각 Apple 또는 Intel CPU에서 Unity UI 재 빌드 및 Canvas 일괄 계산에 대한 매우 심층적 인 프로파일 링을 허용합니다. 메서드 이름은 Unity 프로파일 러 섹션에서 위에서 설명한 프로파일 러 레이블과 거의 동일합니다.
- Canvas :: SendWillRenderCanvases 는 Canvas.SendWillRenderCanvases C# 메서드 를 호출 하고 Unity 프로파일러에서 해당 줄을 관리하는 C++ 부모입니다. 이전 단계에서 설명한대로 Rebuild 프로세스를 실행하는데 사용되는 코드가 포함됩니다.
- Canvas :: UpdateBatches 는 Canvas.BuildBatch 와 동일 하지만 Unity 프로파일러 라벨에 포함되지 않는 추가 상용구 코드(boilerplate code)를 포함합니다. 위에서 설명한 실제 Canvas Batch Building 프로세스를 실행합니다.
IL2CPP를 통해 빌드 된 Unity 앱과 함께 사용하면 이러한 도구를 사용하여 Canvas :: SendWillRenderCanvases의 트랜스 파일 된 C# 코드를 더 자세히 살펴볼 수 있습니다. 주요 관심사는 다음 방법의 비용입니다. (참고 : 트랜스 파일 된 메서드 이름은 대략적인 것입니다.)
- IndexedSet_Sort 및 CanvasUpdateRegistry_SortLayoutList 는 레이아웃이 다시 계산되기 전에 더티 레이아웃 구성 요소 목록을 정렬하는 데 사용됩니다. 위에서 설명한 것처럼 여기에는 각 레이아웃 구성 요소 위의 상위 변환 수를 계산하는 작업이 포함됩니다.
- ClipperRegistry_Cull 은 IClipRegion 인터페이스 의 등록 된 모든 구현 자를 호출합니다 . 내장 구현은 다음과 같습니다 RectMask2D IClippable 인터페이스를 사용합니다. ClipperRegistry.Cull 호출 중에 RectMask2D 구성 요소는 계층 구조에 포함 된 모든 클리핑 가능한 요소를 반복하고 컬링 정보를 업데이트하도록 요청합니다.
- Graphic_Rebuild 에는 이미지, 텍스트 또는 기타 그래픽 파생 구성 요소를 나타내는 데 필요한 메시를 실제로 계산하는 비용이 포함됩니다. 이 같은 여러가지 다른 방법이있을것입니다. (Graphic_UpdateGeometry, Text_OnPopulateMesh)
- Text_OnPopulateMesh 는 일반적으로 최적 맞춤이 활성화 된 경우 핫스팟입니다. 이에 대해서는 이 가이드의 뒷부분에서 자세히 설명합니다.
- Shadow_ModifyMesh 및 Outline_ModifyMesh 와 같은 mesh modifier도 여기에서 실행됩니다. 컴포넌트 드롭 섀도우, 윤곽선 및 기타 특수 효과를 계산하는 비용은 이러한 방법을 통해 확인할 수 있습니다.
Xcode 프레임 디버거 & 인텔 GPA
로우 레벨 프레임 디버깅 도구는 배치 된 UI의 개별 부분 비용을 프로파일 링하고 UI 오버 드로 비용을 모니터링하는 데 필수적입니다. UI 오버 드로에 대해서는이 가이드의 뒷부분에서 자세히 설명합니다.
Xcode 프레임 디버거 사용
주어진 UI가 GPU에 과도한 부담을 주는지 테스트하기 위해 Xcode의 내장 GPU 진단 도구를 사용할 수 있습니다. 먼저 Metal 또는 OpenGLES3를 사용하도록 해당 프로젝트를 구성한 다음 빌드를 만들고 결과 Xcode 프로젝트를 엽니 다. 일부 Xcode 버전 및 기기 조합은 OpenGLES 2 프레임 캡처를 지원할 수 있지만 작동한다는 보장은 없습니다 .
참고 : 일부 버전의 Xcode에서는 그래픽 프로파일 러가 작동하도록 빌드 스키마에서 적절한 그래픽 API를 선택해야합니다. 이렇게하려면 Xcode의 Product 메뉴로 이동하여 Scheme 메뉴 항목을 확장하고 Edit Scheme ...을 선택합니다. Run target을 선택하고 Options 탭으로 이동합니다. 프로젝트에서 사용하는 API와 일치하도록 GPU 프레임 캡처 옵션을 변경합니다. Unity 프로젝트가 그래픽 API를 자동으로 선택하도록 설정되어 있다고 가정하면 대부분의 최신 iPad는 기본적으로 Metal을 사용합니다. 확실하지 않은 경우 프로젝트를 시작하고 Xcode에서 디버그 로그를 확인합니다. 초기 라인 중 하나는 초기화되는 렌더링 경로 (Metal, GLES3 또는 GLES2)를 나타내야합니다.
iOS 장치에서 프로젝트를 빌드하고 실행합니다. GPU 프로파일 러는 Xcode의 Navigator 사이드 바에 디버그 창을 표시하고 FPS 항목을 클릭하여 찾을 수 있습니다.
GPU 프로파일 러의 첫 번째 관심 지점은 화면 중앙에 "Tiler", "Renderer"및 "Device"라는 레이블이 붙은 세 개의 막대 세트입니다. 이 두 가지 중 :
- “Tiler”는 일반적으로 정점 셰이더에 소요 된 시간을 포함하여 지오메트리를 처리하여 GPU가 얼마나 스트레스를 받는지 측정합니다. 일반적으로“Tiler”사용량이 높으면 버텍스 셰이더가 지나치게 느리거나 너무 많은 버텍스가 그려지는 것을 나타냅니다.
- “Renderer”는 일반적으로 GPU의 픽셀 파이프 라인이 얼마나 스트레스를 받는지 측정합니다. 일반적으로“Renderer”사용량이 높으면 응용 프로그램이 GPU의 최대 fill-rate를 초과하거나 비효율적인 fragment 셰이더가 있음을 나타냅니다.
- "Device"는 "Tiler"및 "Renderer"성능을 모두 포함하는 전체 GPU 사용량의 복합측정입니다. 일반적으로 "Tiler"또는 "Renderer"측정 값 중 더 높은 값을 대략적으로 추적하므로 무시할 수 있습니다.
Xcode의 GPU 프로파일 러에 대한 자세한 내용은 이 문서 문서를 참조하십시오 .
Xcode의 프레임 디버거는 GPU 프로파일 러 하단에 숨겨진 작은 '카메라'아이콘을 클릭하여 트리거 할 수 있습니다. 다음 스크린 샷에서 화살표와 빨간색 상자로 강조 표시됩니다.
잠시 후 프레임 디버거의 요약보기가 다음과 같이 나타납니다.
기본 UI 셰이더를 사용할 때 Unity UI 시스템에서 생성 한 지오메트리 렌더링 비용은 기본 UI 셰이더가 커스텀 셰이더로 대체되지 않았다고 가정하고 "UI/Default" 셰이더 패스 아래에 표시됩니다 . 위 스크린 샷에서이 기본 UI 셰이더를 Render Pipeline "UI/Default"로 볼 수 있습니다.
Unity UI는 쿼드만 생성하므로 버텍스 셰이더가 GPU의 타일러 파이프 라인에 스트레스를주지 않을 것입니다. 이 셰이더 패스에 나타나는 문제는 fill-rate 문제로 인한 것일 수 있습니다.
프로파일러 결과 분석
프로파일 링 데이터를 수집 한 후 몇 가지 결론을 도출 할 수 있습니다.
Canvas.BuildBatch 또는 Canvas::UpdateBatches은 과도한 CPU 시간을 사용하는 것의 경우. 하나의 캔버스에 캔버스 렌더러 컴포넌트의 과도한 숫자입니다. 캔버스 단계의 캔버스 분할 섹션을 참조하십시오.
GPU에서 UI를 그리는 데 과도한 시간이 소요되고 프레임 디버거가 프래그먼트 셰이더 파이프 라인에 병목 현상이 있음을 나타내는 경우 UI가 GPU가 처리 할 수 있는 픽셀 fill-rate 속도를 초과 할 가능성이 있습니다. 가장 가능성이 높은 원인은 과도한 UI 오버 드로입니다. 채우기 비율, 캔버스 및 입력 단계의 fill-rate 문제 해결 섹션을 참조하세요.
Canvas.SendWillRenderCanvases 또는 Canvas :: SendWillRenderCanvases로 이동하는 CPU 시간의 상당 부분에서 볼 수 있듯이 그래픽 재 빌드가 과도한 CPU를 사용하는 경우 더 깊은 분석이 필요합니다. 그래픽 rebuild 프로세스의 일부가 원인 일 가능성이 있습니다.
WillRenderCanvas의 많은 부분이 IndexedSet_Sort 또는 CanvasUpdateRegistry_SortLayoutList 내에서 소비되는 경우 더티 레이아웃 구성 요소 목록을 정렬하는 데 시간이 소요됩니다. 캔버스에서 레이아웃 컴포넌트의 수를 줄이는 것이 좋습니다. 가능한 수정 사항은 RectTransforms로 레이아웃 변경 및 캔버스 분할 섹션을 참조하십시오.
Text_OnPopulateMesh 에서 과도한 시간이 소요되는 것처럼 보이면 범인은 단순히 텍스트 메시 생성입니다. 가능한 수정 사항에 대해서는 최적 맞춤 및 캔버스 비활성화 섹션을 참조하고 재작성중인 텍스트의 대부분이 실제로 기본 문자열 데이터가 변경되지 않은 경우 캔버스 분할 내부의 조언을 고려하십시오.
시간이 내부 소요되는 경우 Shadow_ModifyMesh 또는 Outline_ModifyMesh (또는 다른 구현 ModifyMesh ), 다음 문제는 메쉬 수식을 계산하는 동안 과도한 시간입니다. 이러한 컴포넌트를 제거하고 static 이미지를 통해 시각적 효과를 얻으십시오.
Canvas.SendWillRenderCanvases 내에 특정 핫스팟이 없거나 모든 프레임을 실행하는 것처럼 보이는 경우 동적 요소가 정적 요소와 함께 그룹화되어 전체 Canvas를 너무 자주 다시 빌드해야하는 문제일 수 있습니다. 캔버스 분할 단계를 참조하십시오.
4. Fill-rate, Canvases and input
이 장에서는 Unity UI 구조화와 관련된 광범위한 문제에 대해 설명합니다.
Fill-Rate 이슈관련
GPU의 fragment 파이프 라인에 대한 스트레스를 줄이기 위해 취할 수있는 두 가지 조치 과정이 있습니다.
- fragment셰이더의 복잡성을 줄입니다. 자세한 내용은 "UI 셰이더 및 저사양 장치"섹션을 참조하십시오.
- 샘플링해야하는 픽셀 수를 줄입니다.
UI 셰이더는 일반적으로 표준화되어 있으므로 가장 일반적인 문제는 단순히 과도한 fill-rate 사용입니다. 이는 대부분의 겹치는 UI 요소가 많거나 화면의 상당 부분을 차지하는 여러 UI 요소가 있기 때문입니다. 이 두 가지 문제 모두 매우 높은 수준의 overdraw로 이어질 수 있습니다.
유효 fillrate 과다 사용을 완화하고 overdraw을 줄이려면 다음과 같은 가능한 수정 사항을 고려하십시오.
보이지 않는 UI 제거
기존 UI 요소를 최소한으로 재디자인해야하는 방법은 플레이어에게 표시되지 않는 요소를 비활성화하는 것입니다. 이것이 적용되는 가장 일반적인 경우는 불투명한 배경으로 전체 화면 UI를 여는 것입니다. 이 경우 전체 화면 UI 아래에 배치 된 모든 UI 요소를 비활성화 할 수 있습니다.
이를 수행하는 가장 간단한 방법은 보이지 않는 UI 요소를 포함하는 루트 게임 오브젝트 또는 게임 오브젝트를 비활성화하는 것입니다. 대체 솔루션은 캔버스 비활성화 섹션을 참조하십시오 .
마지막으로 요소가 여전히 GPU로 전송되고 귀중한 렌더링 시간이 걸릴 수 있으므로 알파를 0으로 설정하여 숨겨진 UI 요소가 없는지 확인합니다. UI 요소에 그래픽 컴포넌트가 필요하지 않은 경우 간단히 제거하면 레이캐스팅이 계속 작동합니다.
UI 구조 단순화
UI를 다시 빌드하고 렌더링하는 데 필요한 시간을 줄이려면 UI 개체 수를 가능한 한 적게 유지하는 것이 중요합니다. 가능한 한 많이 bake하십시오. 예를 들어 혼합 된 GameObject를 사용하여 색상을 요소로 변경하지 말고 대신 material 속성을 통해 수행하십시오. 또한 폴더처럼 작동하고 씬을 구성하는 것 외에 다른 목적이 없는 게임 오브젝트를 생성하지 마십시오.
보이지 않는 카메라 출력 비활성화
불투명 한 배경이있는 전체 화면 UI가 열리면 월드 스페이스 카메라는 여전히 UI 뒤에서 표준 3D 장면을 렌더링합니다. 렌더러는 전체 화면 Unity UI가 전체 3D 장면을 가리는 것을 인식하지 못합니다.
따라서 완전히 전체 화면 UI가 열리면 가려진 세계 공간 카메라를 모두 비활성화하면 쓸모없는 3D 세계 렌더링 작업을 제거하여 GPU 스트레스를 줄이는 데 도움이됩니다.
UI가 전체 3D 장면을 다루지 않는 경우 Scene을 텍스처로 한 번 렌더링해서 사용하는것이 연속적으로 렌더링하는 것보다 좋습니다. 3D 장면에서 애니메이션 콘텐츠를 볼 수있는 가능성을 잃게되지만 대부분의 경우 허용됩니다.
참고 : Canvas가 “Screen Space–Overlay” 로 설정되어 있으면 Scene에서 활성화 된 카메라 수에 관계없이 그려집니다.
대다수가 가려진 카메라
많은 "전체 화면" UI는 실제로 전체 3D 세계를 가리지 않지만 세계의 작은 부분을 표시합니다. 이러한 경우 렌더 텍스처에 표시되는 세계 부분 만 캡처하는 것이 더 최적 일 수 있습니다. 세계의 보이는 부분이 렌더 텍스처에서 "캐시"되면 실제 세계 공간 카메라를 비활성화 할 수 있으며 캐시된 렌더 텍스처를 UI 화면 뒤에 그려 3D 세계의 가짜버전을 제공 할 수 있습니다.
Composition-based UIs
디자이너가 구성을 통해 UI를 만드는 것은 매우 일반적입니다. 표준 배경과 요소를 결합하고 레이어링하여 최종 UI를 만듭니다. 이 작업은 비교적 간단하고 반복 작업에 매우 친숙하지만 Unity UI의 transparent rendering queue 사용으로 인해 성능이 떨어집니다.
배경, 버튼 및 버튼의 일부 텍스트가 있는 간단한 UI를 고려하십시오. 투명 대기열의 객체는 뒤에서 앞으로 정렬되기 때문에 픽셀이 텍스트 글리프 내에있는 경우 GPU는 배경 텍스처, 버튼 텍스처, 마지막으로 텍스트 아틀라스 텍스처를 샘플링해야합니다. 세 개의 샘플. UI의 복잡성이 증가하고 더 많은 장식 요소가 배경에 겹쳐지면서 샘플 수가 빠르게 증가 할 수 있습니다.
큰 UI가 fill-rate가 제한되는 것으로 확인되면 가장 좋은 방법은 UI의 decorative/invariant(장식/불변)요소를 배경 텍스처에 병합하는 특수 UI 스프라이트를 만드는 것입니다. 이렇게하면 원하는 디자인을 얻기 위해 서로 겹쳐야하는 요소의 수가 줄어들지만 노동 집약적이며 프로젝트의 텍스처 아틀라스 크기가 증가합니다.
특정 UI를 생성하는 데 필요한 레이어 요소 수를 특수 UI 스프라이트에 압축하는이 원칙은 하위 요소에도 사용할 수 있습니다. 제품의 스크롤 창이있는 상점 UI를 고려하십시오. 각 제품 UI 요소에는 테두리, 배경 및 가격, 이름 및 기타 정보를 나타내는 일부 아이콘이 있습니다.
스토어 UI에는 배경이 필요하지만 제품이 배경을 가로 질러 스크롤되기 때문에 제품 요소를 스토어 UI의 배경 텍스처에 병합 할 수 없습니다. 그러나 제품 UI 요소의 테두리, 가격, 이름 및 기타 요소는 제품의 배경에 병합 될 수 있습니다. 아이콘의 크기와 수에 따라 유효 노출 률을 크게 줄일 수 있습니다.
계층화 된 요소를 결합하는 데는 몇 가지 단점이 있습니다. specialized 된 요소는 더 이상 재사용 할 수 없으며 생성하려면 추가 아티스트 리소스가 필요합니다. 큰 새 텍스처를 추가하면 특히 UI 텍스처가 요청시 로드 및 언로드되지 않는 경우 UI 텍스처를 유지하는 데 필요한 메모리 양이 크게 증가 할 수 있습니다.
UI 셰이더 및 저사양 장치
Unity UI에서 사용하는 내장 셰이더는 마스킹, 클리핑 및 기타 여러 복잡한 작업에 대한 지원을 통합합니다. 이러한 복잡성이 추가 되었기 때문에 UI 셰이더는 iPhone 4와 같은 저가형 장치에서 더 단순한 Unity 2D 셰이더에 비해 성능이 떨어집니다.
저가형 장치를 대상으로하는 응용 프로그램에 마스킹, 클리핑 및 기타 "멋진"기능이 필요하지 않은 경우 다음과 같은 최소 UI 셰이더와 같이 사용되지 않는 작업을 생략하는 사용자 지정 셰이더를 만들 수 있습니다.
Shader "UI/Fast-Default" {
Properties
{
[PerRendererData]
_MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.worldPosition = IN.vertex;
OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition);
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
return (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
}
ENDCG
}
}
}
UI Canvas rebuilds
UI를 표시하려면 UI 시스템이 화면에 표시되는 각 UI 컴포넌트에 대한 지오메트리를 구성해야합니다. 여기에는 동적 레이아웃 코드 실행, UI 텍스트 문자열에서 문자를 나타내는 다각형 생성, 드로 콜을 최소화하기 위해 가능한 한 많은 지오메트리를 단일 메시로 병합하는 것이 포함됩니다. 이는 multi-step 프로세스이며이 가이드의 시작 부분에있는 기본 섹션에 자세히 설명되어 있습니다.
캔버스 재 구축은 다음 두 가지 주요 이유로 성능 문제가 될 수 있습니다.
- Canvas의 드로어블 UI 요소 수가 많으면 배치 자체를 계산하는 데 많은 비용이 듭니다. 이는 요소를 정렬하고 분석하는 데 드는 비용이 캔버스의 드로어 블 UI 요소 수에 비례하여 증가하기 때문입니다.
- Canvas가 자주 변경되면(dirty) 상대적으로 변경 사항이 적은 Canvas를 새로 고치는 데 과도한 시간이 소요될 수 있습니다.
이러한 문제는 모두 Canvas의 element수가 증가함에 따라 심각 해지는 경향이 있습니다.
중요 : 주어진 Canvas의 드로어블 UI 요소가 변경 될 때마다 Canvas는 batch빌드 프로세스를 다시 실행해야합니다. 이 프로세스는 변경 여부에 관계없이 Canvas의 모든 드로어블 UI 요소를 다시 분석합니다. "변경"은 스프라이트 렌더러에 할당 된 스프라이트, 변형 위치 및 배율, 텍스트 메시에 포함 된 텍스트 등을 포함하여 UI 개체의 모양에 영향을 주는 모든 변경 사항입니다.
자식오브젝트 순서
Unity UI는 정렬 순서를 결정하는 계층 구조에서 객체의 순서로 뒤에서 앞으로 구성됩니다. 계층 구조의 앞부분에있는 개체는 계층 구조의 뒷부분에있는 개체로 간주됩니다. 배치는 계층 구조를 위에서 아래로 걷고 동일한 재료, 동일한 텍스처를 사용하고 중간 레이어가없는 모든 오브젝트를 수집하여 빌드됩니다. "중간 레이어"는 재질이 다른 그래픽 개체로, 경계 상자가 다른 배치 가능한 두 개체와 겹치고 두 개의 배치 가능한 개체 사이의 계층 구조에 배치됩니다. 중간 레이어로 인해 일괄 처리가 중단됩니다.
Unity UI 프로파일 링 도구 단계에서 설명했듯이 UI 프로파일 러와 프레임 디버거를 사용하여 UI에서 중간 레이어를 검사 할 수 있습니다. 이것은 하나의 드로어 블 객체가 배치 가능한 다른 두 드로어 블 객체 사이에 끼어 드는 상황입니다.
이 문제는 텍스트와 스프라이트가 서로 가까이있을 때 가장 일반적으로 발생합니다. 텍스트 글리프의 다각형의 대부분이 투명하기 때문에 텍스트의 경계 상자가 근처의 스프라이트와 눈에 띄지 않게 겹칠 수 있습니다. 이것은 두 가지 방법으로 해결할 수 있습니다.
- 드로어 블을 재정렬하여 배치 가능한 객체가 batch 불가능한 객체에 의해 개재되지 않도록합니다. 즉, 일괄 처리 할 수없는 개체를 일괄 처리 가능한 개체의 위나 아래로 이동합니다.
- 보이지 않는 겹치는 공간을 제거하기 위해 개체의 위치를 조정합니다.
이 두 작업은 Unity 프레임 디버거가 열려 있고 활성화 된 상태에서 Unity 에디터에서 수행 할 수 있습니다. Unity 프레임 디버거에서 볼 수있는 드로 콜의 수를 관찰하기 만하면 겹치는 UI 요소로 인해 낭비되는 드로 콜 수를 최소화하는 순서와 위치를 찾을 수 있습니다.
캔버스 분할
가장 사소한 경우를 제외하고는 일반적으로, 요소를 하위 캔버스 또는 형제 캔버스로 이동하여 캔버스를 분할하는 것이 좋습니다.
형제 캔버스는 UI의 특정 부분이 나머지 UI와 별도로 그리기 깊이를 제어해야하는 경우에 가장 적합하며 항상 다른 레이어 (예 : 튜토리얼 화살표)의 위 또는 아래에 있습니다.
다른 대부분의 경우 하위 캔버스는 상위 캔버스에서 표시 설정을 상속하므로 더 편리합니다.
언뜻보기에는 UI를 여러 하위 캔버스로 세분화하는 것이 가장 좋은 방법 인 것처럼 보일 수 있지만 캔버스 시스템은 별도의 캔버스에 배치를 결합하지 않습니다. 성능이 뛰어난 UI 디자인에는 rebuild 비용 최소화와 낭비되는 드로콜 최소화 사이의 균형이 필요합니다.
일반 지침
Canvas는 구성되는 드로어블 구성 요소가 변경 될 때마다 다시 배치되기 때문에 일반적으로 중요하지 않은 Canvas를 두 개 이상의 부분으로 분할하는 것이 가장 좋습니다. 또한 요소가 동시에 변경 될 것으로 예상되는 경우 동일한 Canvas에서 요소를 함께 배치하는 것이 가장 좋습니다. 예를 들어 진행률 표시 줄과 카운트 다운 타이머가 있습니다. 둘 다 동일한 기본 데이터에 의존하므로 동시에 업데이트가 필요하므로 동일한 Canvas에 배치해야합니다.
하나의 Canvas에 배경 및 label과 같이 정적이고 변경되지 않는 모든 요소를 배치합니다. 캔버스가 처음 표시 될 때 한 번 batch처리되며 이후에 더 이상 다시 batch처리 할 필요가 없습니다.
두 번째 Canvas에는 자주 변경되는 모든 "동적"요소를 배치합니다. 이렇게하면이 Canvas가 주로 더러운 요소를 다시 배치합니다. 동적 요소의 수가 매우 커지면 동적 요소를 지속적으로 변경되는 요소 집합 (예 : 진행률 표시 줄, 타이머 판독 값, 애니메이션)과 가끔씩만 변경되는 요소 집합으로 더 세분화해야 할 수 있습니다.
이것은 실제로는 다소 어렵습니다. 특히 UI 컨트롤을 프리 팹으로 캡슐화 할 때 더욱 그렇습니다. 대신 많은 UI가 비용이 많이 드는 컨트롤을 하위 캔버스로 분할하여 캔버스를 세분화하도록 선택합니다.
Unity 5.2 및 최적화 된 배치
Unity 5.2에서 배치 코드는 실질적으로 재 작성되었으며 Unity 4.6, 5.0 및 5.1에 비해 훨씬 더 성능이 뛰어납니다. 또한 코어가 2 개 이상인 장치에서 Unity UI 시스템은 대부분의 처리를 작업자 스레드로 이동합니다. 일반적으로 Unity 5.2는 UI를 수십 개의 하위 캔버스로 적극적으로 분할해야하는 필요성을 줄여줍니다. 이제 모바일 장치의 많은 UI를 2~3 개의 캔버스로 성능을 향상시킬 수 있습니다.
Unity 5.2의 최적화에 대한 자세한 내용은 이 블로그 게시물 에서 확인할 수 있습니다 .
Unity UI의 Input및 레이캐스팅
기본적으로 Unity UI는 그래픽 Raycaster 구성 요소를 사용하여 터치 이벤트 및 포인터 호버 이벤트와 같은 입력 이벤트를 처리합니다. 이것은 일반적으로 독립 실행 형 입력 관리자 구성 요소에 의해 처리됩니다. 이름에도 불구하고 독립 실행 형 입력 관리자는 "universal"입력 관리자 시스템을 의미하며 포인터와 터치를 모두 처리합니다.
레이캐스트 최적화
Graphic Raycaster는 'Raycast Target'설정이 true로 설정된 모든 그래픽 구성 요소를 반복하는 비교적 간단한 구현입니다. 각 Raycast Target에 대해 Raycaster는 일련의 테스트를 수행합니다. Raycast Target이 모든 테스트를 통과하면 적중 목록에 추가됩니다.
Raycast 구현 세부 사항
테스트는 다음과 같습니다.
- Raycast Target이 active, enable되고 draw된경우 (예 : 지오메트리가 있음)
- 입력 포인트가 Raycast Target이 연결된 RectTransform 내에있는 경우
- Raycast Target에 ICanvasRaycastFilter 컴포넌트가 있거나 자식 (깊이에 관계없이) 이고 해당 Raycast 필터 컴포넌트가 Raycast를 허용하는 경우.
hit 레이 캐스트 타겟 목록은 깊이별로 정렬한후, 반전된 타겟에 대해 필터링되며 카메라 뒤에 렌더링 된 요소 (즉, 화면에 표시되지 않음)가 제거되도록 필터링됩니다.
그래픽 Raycaster는 또한 그래픽 Raycaster의 "Blocking Objects" 속성 에 해당 플래그가 설정되어있는 경우 3D 또는 2D 물리 시스템에 Raycast할 수 있습니다. (스크립트에서 속성 이름은 blockingObjects 입니다.)
2D 또는 3D blocking 오브젝트가 활성화 된 경우 레이캐스트 차단 물리레이어의 2D 또는 3D 오브젝트 아래에 그리는 모든 레이 캐스트 대상도 히트 목록에서 제거됩니다.
그런 다음 최종 적중 목록이 반환됩니다.
레이 캐스팅 최적화 팁
모든 Raycast Target은 Graphic Raycaster에서 테스트해야하므로 포인터 이벤트를 수신해야하는 UI 구성 요소에서만 'Raycast Target'설정을 활성화하는 것이 좋습니다. Raycast 대상 목록이 작고 통과해야하는 계층 구조가 얕을수록 각 Raycast 테스트가 더 빨라집니다.
배경과 텍스트가 모두 색상을 변경하려는 버튼과 같이 포인터 이벤트에 응답해야하는 여러 드로어 블 UI 개체가있는 복합 UI 컨트롤의 경우 일반적으로 복합 UI의 루트에 단일 Raycast Target을 배치하는 것이 좋습니다. 단일 Raycast Target이 포인터 이벤트를 수신하면 복합 컨트롤 내의 각 관련 구성 요소에 이벤트를 전달할 수 있습니다.
계층 깊이 및 레이 캐스트 필터
각 그래픽 레이 캐스트는 레이 캐스트 필터를 검색 할 때 루트까지 Transform 계층을 횡단합니다. 이 작업의 비용은 계층 구조의 깊이에 비례하여 선형 적으로 증가합니다. 계층 구조의 각 Transform에 연결된 모든 구성 요소는 ICanvasRaycastFilter 를 구현하는지 테스트해야 하므로 저렴한 작업이 아닙니다.
CanvasGroup , Image , Mask 및 RectMask2D 와 같이 ICanvasRaycastFilter를 사용하는 몇 가지 표준 Unity UI 구성 요소가 있으므로 이 순회를 간단하게 제거 할 수 없습니다.
하위 캔버스 및 OverrideSorting 속성
하위 캔버스 의 overrideSorting 속성은 그래픽 레이 캐스트 테스트가 변환 계층 구조 상승을 중지하도록합니다. 정렬 또는 레이 캐스트 감지 문제를 일으키지 않고 활성화 할 수 있다면 레이 캐스트 계층 구조 순회 비용을 줄이는 데 사용해야합니다.
5. UI 컨트롤 최적화
UI Text
Unity의 내장 텍스트 컴포넌트는 UI 내에서 래스터 화 된 텍스트 글리프를 표시하는 편리한 방법입니다. 그러나 일반적으로 알려지지 않았지만 성능 핫스팟으로 자주 나타나는 동작이 많이 있습니다. UI에 텍스트를 추가 할 때 텍스트 글리프는 실제로 문자 당 하나씩 개별 쿼드로 렌더링된다는 점을 항상 기억하십시오. 이 쿼드는 모양에 따라 글리프 주위에 상당한 양의 빈 공간이있는 경향이 있으며 의도하지 않게 다른 UI 요소의 일괄 처리를 중단하는 방식으로 텍스트를 배치하는 경우가 많습니다.
Text mesh rebuilds
한 가지 주요 문제는 UI 텍스트 메시를 재 구축하는 것입니다. UI 텍스트 구성 요소가 변경 될 때마다 텍스트 구성 요소는 실제 텍스트를 표시하는 데 사용되는 다각형을 다시 계산해야합니다. 이 재계산은 텍스트 컴포넌트 또는 부모 게임 오브젝트가 텍스트 변경없이 단순히 비활성화되고 다시 활성화 된 경우에도 발생합니다.
이 동작은 많은 수의 텍스트 레이블을 표시하는 모든 UI에서 문제가되며 가장 일반적인 것은 리더 보드 또는 통계 화면입니다. Unity UI를 숨기고 표시하는 가장 일반적인 방법은 UI가 포함 된 게임 오브젝트를 활성화 / 비활성화하는 것이므로 텍스트 구성 요소가 많은 UI는 표시 될 때마다 원하지 않는 프레임 속도 딸꾹질을 유발하는 경우가 많습니다.
이 문제에 대한 잠재적 인 해결 방법은 다음 단계의 캔버스 비활성화 섹션을 참조하십시오.
Dynamic fonts and font atlases
다이나믹폰트는 표시 가능한 전체 문자 집합이 매우 크거나 런타임 이전에 알려지지 않은 경우 텍스트를 표시하는 편리한 방법입니다. Unity 구현에서 이러한 글꼴은 UI 텍스트 구성 요소 내에서 발생하는 문자를 기반으로 런타임에 글리프 아틀라스를 빌드합니다.
로드된 각각의 고유 한 Font 객체는 다른 글꼴과 동일한 글꼴 패밀리에 있더라도 자체 텍스처 아틀라스를 유지합니다. 예를 들어, 한 컨트롤에서 굵은 텍스트로 Arial을 사용하고 다른 컨트롤에서 Arial Bold를 사용하면 동일한 출력이 생성되지만 Unity는 Arial과 Arial Bold에 대해 하나씩 두 개의 별개의 텍스처 아틀라스를 유지합니다.
성능 관점에서 이해해야 할 가장 중요한 점은 Unity UI의 다이나믹폰트가 크기, 스타일 및 문자의 각 고유 한 조합에 대해 글꼴의 텍스처 아틀라스에 하나의 글리프를 유지한다는 것입니다. 즉, UI에 문자 'A'를 표시하는 두 개의 텍스트 구성 요소가 포함 된 경우 :
- 두 텍스트 구성 요소가 같은 크기를 공유하는 경우 글꼴 아틀라스에는 하나의 글리프가 있습니다.
- 두 텍스트 구성 요소가 동일한 크기를 공유하지 않는 경우 (예 : 하나는 16 포인트, 다른 하나는 24 포인트) 글꼴 아틀라스에는 서로 다른 크기의 문자 'A'의 두 복사본이 포함됩니다.
- 하나의 텍스트 구성 요소가 굵게 표시되고 다른 구성 요소는 그렇지 않은 경우 글꼴 아틀라스에는 굵은 'A'와 일반 'A'가 포함됩니다.
다이나믹폰트가 있는 UI텍스트 객체가 글꼴의 텍스처 아틀라스로 아직 래스터 화되지 않은 글리프를 만날 때마다 글꼴의 텍스처 아틀라스를 다시 작성해야합니다. 새 글리프가 현재 아틀라스에 맞으면 추가되고 아틀라스가 그래픽 장치에 다시 업로드됩니다. 그러나 현재 아틀라스가 너무 작으면 시스템이 아틀라스를 다시 빌드하려고 시도합니다. 이것은 두 단계로 이루어집니다.
첫째, 아틀라스는 현재 활성화된 UI 텍스트 구성 요소에 의해 표시되는 글리프 만 사용하여 동일한 크기로 rebuild됩니다 . 여기에는 부모 캔버스가 활성화되었지만 캔버스 렌더러가 비활성화된 UI 텍스트 구성 요소가 포함됩니다. 시스템이 현재 사용중인 모든 글리프를 새 아틀라스에 맞추는 데 성공하면 해당 아틀라스를 래스터 화하고 두 번째 단계로 계속 진행하지 않습니다.
둘째, 현재 사용중인 글리프 세트를 현재 아틀라스와 같은 크기의 아틀라스에 맞출 수없는 경우 아틀라스의 더 짧은 차원을 두 배로 늘려 더 큰 아틀라스가 생성됩니다. 예를 들어 512x512 아틀라스는 512x1024 아틀라스로 확장됩니다.
위의 알고리즘으로 인해 동적 글꼴의 아틀라스는 생성 된 후에 만 크기가 커집니다. 텍스처 아틀라스를 다시 빌드하는 비용을 감안할 때 다시 빌드하는 동안 최소화하는 것이 필수적입니다. 이것은 두 가지 방법으로 수행 할 수 있습니다.
가능하면 다이나믹폰트를 사용하지않고 원하는 글리프 세트에 대한 지원을 미리 구성하십시오. 이는 일반적으로 라틴 / ASCII 문자와 같이 제한적인 문자 집합을 사용하고 크기가 작은 UI에 적합합니다.
전체 유니 코드 세트와 같이 매우 큰 범위의 문자를 지원해야하는 경우 글꼴을 동적으로 설정해야합니다. 예측 가능한 성능 문제를 방지하려면 시작시 Font.RequestCharactersInTexture 를 통해 적절한 문자 집합으로 글꼴의 글리프 아틀라스를 프라이밍 합니다.
글꼴 아틀라스 재구성은 변경된 각 UI 텍스트 구성 요소에 대해 개별적으로 트리거됩니다. 매우 많은 수의 텍스트 구성 요소를 채울 때 텍스트 구성 요소의 콘텐츠에서 모든 고유 문자를 수집하고 글꼴 아틀라스를 프라이밍하는 것이 유리할 수 있습니다. 이렇게하면 글리프 아틀라스가 새 글리프를 만날 때마다 다시 빌드되는 대신 한 번만 다시 빌드하면됩니다.
또한 글꼴 아틀라스 rebuild가 트리거되면 현재 활성화된 UI 텍스트 컴포넌트에 포함되지 않은 모든 문자가 새 아틀라스에 나타나지 않습니다. Font.RequestCharactersInTexture에 대한 호출의 결과로 아틀라스에 원래 추가 된 경우에도 마찬가지입니다. 이 제한을 해결하려면 Font.textureRebuilt 대리자를 구독하고 Font.characterInfo를 쿼리하여 원하는 모든 문자가 준비 상태로 유지되도록합니다.
Font.textureRebuilt 대리자는 현재 문서화되지 않았습니다. 단일 인수 Unity 이벤트입니다. 인수는 텍스처가 다시 빌드 된 글꼴입니다. 이 이벤트의 구독자는 다음 서명을 따라야합니다.
public void TextureRebuiltCallback(Font rebuiltFont) { /* ... */ }
Specialized glyph renderers
글리프가 잘 알려진 상황에서 각 글리프 사이에 상대적으로 고정 된 위치가있는 경우 해당 글리프를 표시하는 스프라이트를 표시하는 사용자 지정 구성 요소를 작성하는 것이 훨씬 더 유리합니다. 이에 대한 예는 점수 표시 일 수 있습니다.
점수의 경우 표시 가능한 문자는 잘 알려진 글리프 세트 (숫자 0-9)에서 그려지며 지역간에 변경되지 않고 서로 고정 된 거리에 나타납니다. 정수를 숫자로 분해하고 적절한 숫자 스프라이트를 표시하는 것은 상대적으로 간단합니다. 이러한 종류의 특수한 숫자 표시 시스템은 Canvas 기반 UI 텍스트 구성 요소보다 할당이없고 계산, 애니메이션 및 표시가 상당히 빠른 방식으로 구축 될 수 있습니다.
Fallback fonts and memory usage
큰 문자 집합을 지원해야하는 응용 프로그램의 경우 글꼴 가져 오기 프로그램의 "글꼴 이름"필드에 많은 글꼴을 나열하는 것이 좋습니다. “글꼴 이름”필드에 나열된 모든 글꼴은 글리프를 기본 글꼴 내에 찾을 수없는 경우 대체로 사용됩니다. 대체 순서는 "글꼴 이름"필드에 글꼴이 나열되는 순서에 따라 결정됩니다.
그러나 이 동작을 지원하기 위해 Unity는 메모리에로드 된 "Font Names"필드에 나열된 모든 글꼴을 유지합니다. 글꼴의 문자 집합이 매우 크면 대체 글꼴에 사용되는 메모리 양이 과도해질 수 있습니다. 이것은 일본어 한자 또는 중국어 문자와 같은 그림 글꼴을 포함 할 때 가장 자주 나타납니다.
Best Fit and performance
일반적으로 UI 텍스트 컴포넌트의 Bestfit 설정은 사용해서는 안됩니다.
"최적 맞춤"은 글꼴 크기를 구성 가능한 최소 / 최대 포인트 크기로 고정 된 오버플로없이 텍스트 구성 요소의 경계 상자 내에 표시 할 수있는 가장 큰 정수 포인트 크기로 동적으로 조정합니다. 그러나 Unity는 표시되는 문자의 고유 한 크기에 대해 고유 한 글리프를 글꼴 아틀라스로 렌더링하기 때문에 Best Fit을 사용하면 여러 다른 글리프 크기로 아틀라스를 빠르게 압도 할 수 있습니다.
Unity 2017.3부터 Best Fit에서 사용하는 크기 감지는 최적이 아닙니다. 테스트된 각 크기 증분에 대해 글꼴 아틀라스에 글리프를 생성하므로 글꼴 아틀라스를 생성하는 데 필요한 시간이 더 늘어납니다. 또한 아틀라스 오버플로를 유발하여 오래된 글리프가 아틀라스에서 쫓겨나 게하는 경향이 있습니다. 최적 맞춤 계산에 필요한 많은 테스트로 인해 다른 텍스트 구성 요소에서 사용중인 글리프를 제거하고 적절한 글꼴 크기가 계산 된 후 적어도 한 번 더 글꼴 아틀라스를 다시 작성해야합니다. 이 특정 문제는 Unity 5.4에서 수정되었으며 Best Fit은 글꼴의 텍스처 아틀라스를 불필요하게 확장하지는 않지만 여전히정적 인 크기의 텍스트보다 상당히 느립니다.
글꼴 아틀라스를 자주 다시 빌드하면 런타임 성능이 빠르게 저하되고 메모리 조각화가 발생합니다. Best Fit으로 설정된 텍스트 구성 요소의 양이 많을수록이 문제는 더욱 악화됩니다.
TextMeshPro Text
TextMesh Pro (TMP)는 텍스트 메시 및 UI 텍스트와 같은 Unity의 기존 텍스트 구성 요소를 대체합니다. TextMesh Pro는 기본 텍스트 렌더링 파이프 라인으로 SDF (Signed Distance Field)를 사용하여 모든 포인트 크기와 해상도에서 텍스트를 깔끔하게 렌더링 할 수 있습니다. TextMesh Pro는 SDF 텍스트 렌더링의 기능을 활용하도록 설계된 사용자 정의 셰이더 세트를 사용하여 단순히 material속성을 변경하여 확장, 윤곽선, 부드러운 그림자, 베벨링과 같은 시각적 스타일을 추가함으로써 텍스트의 시각적 모양을 동적으로 변경할 수 있습니다. 텍스처, 글로우 등을 사용하고 material사전 설정을 생성/사용하여 이러한 비주얼 스타일을 저장하고 불러옵니다.
Text mesh rebuilds
Unity의 내장 UIText 구성 요소와 마찬가지로 구성 요소에 표시되는 텍스트를 변경하면 비용이 많이들 수있는 Canvas.SendWillRendererCanvases 및 Canvas.BuildBatch 호출이 트리거됩니다. TextMeshProUGUI 구성 요소의 텍스트 필드에 대한 변경을 최소화하고 텍스트가 자주 변경되는 부모 TextMeshProUGUI 구성 요소를 자체 Canvas 구성 요소가있는 부모 게임 오브젝트로 변경하여 Canvas 리빌드 호출이 가능한 한 효율적으로 유지되도록합니다.
Worldspace에 표시되는 텍스트의 경우 사용자가 TextMeshProUGUI를 사용하는 대신 일반 TextMeshPro 구성 요소를 사용하는 것이 좋습니다. Worldspace에서 Canvas를 사용하는 것은 비효율적 일 수 있습니다. 캔버스 시스템 오버 헤드가 발생하지 않기 때문에 TextMeshPro를 직접 사용하는 것이 더 효율적입니다.
Fonts and memory 사용량
TMP에는 동적 글꼴 기능이 없으므로 fallback font를 사용해야합니다. fallback font가 로드되고 사용되는 방식을 이해하는 것은 TMP를 사용할 때 메모리를 최적화하는 데 중요합니다.
TMP의 글리프 검색은 반복적으로 수행됩니다. 즉, TMP font asset에서 글리프가 누락된 경우 TMP는 목록의 첫번째 대체(fallback)부터 시작하여 자체 대체를 통해 현재 할당되었거나 활성화된 대체 글꼴 자산의 목록을 반복합니다. 그래도 글리프가 발견되지 않으면 TMP는 이 스프라이트 애셋에 할당된 모든 fallback과 함께 텍스트 오브젝트에 잠재적으로 할당 된 스프라이트 애셋을 검색합니다. 그래도 원하는 글리프를 찾을 수없는 경우 TMP는 TMP 설정 파일에 할당된 일반 대체 목록과 기본 스프라이트 애셋을 차례로 검색합니다. 그래도 이 글리프를 찾을 수없는 경우 TMP 설정에 할당 된 기본 글꼴 자산을 검색합니다. 마지막 수단으로 TMP는 TMP 설정 파일에 정의되어있는 누락된 글리프 교체 문자를 사용하고 표시합니다.
TextMesh Pro의 글꼴 자산은 Scene 또는 프로젝트에서 참조 될 때 로드됩니다. 주로 TextMeshPro 텍스트 구성 요소, TMP 설정 및 글꼴 자산 자체에서 대체 글꼴로 참조됩니다. 글꼴 자산이 TMP 설정 자산에서 참조되는 경우 해당 글꼴 자산과 모든 대체 글꼴 자산은 TMP 텍스트 구성 요소가있는 첫 번째 장면이 활성화 될 때 재귀적으로 로드됩니다. 기본 스프라이트 시트 자산이 참조되는 경우 해당 자산도 로드됩니다.
추가로, 폰트 애셋이 주어진 씬의 TextMeshPro 컴포넌트에 의해 참조되고 TMP 설정을 통해 로드되지 않은 경우, 참조된 폰트 애셋과 모든 대체 폰트 애셋은 컴포넌트가 활성화되면 재귀적으로 로드됩니다. 글꼴이 많은 프로젝트에서 작업 할 때 특히 사용 가능한 메모리가 문제인 경우 이 프로세스를 염두에 두는 것이 중요합니다.
위의 이유로 TMP를 사용할 때 프로젝트를 현지화하는 것은 TMP설정을 통해 모든 현지화 된 언어 글꼴 자산을 미리로드하는 것이 메모리 압력에 해로울 수 있기 때문에 문제가됩니다. 현지화가 필요한 경우 필요한 경우에만 이러한 글꼴 자산 또는 폴백을 할당하거나 (다양한 장면이로드됨에 따라) 자산 번들을 사용하여 모듈 방식으로 글꼴 자산을 로드하는 잠재적 전략을 제안합니다.
애플리케이션이 시작될 때 사용자의 로케일을 확인하고 각 글꼴 자산에 대한 글꼴 자산 대체를 설정하기위한 부트 스트랩 단계가 포함되어야합니다.
- 기본 TMP Font Asset에 대한 Assetbundle 생성 (예 : 각 글꼴에 대한 최소 라틴 글리프)
- 언어 당 필요한 폴백 TMP 폰트어셋에 대한 Assetbundle을 생성합니다 (예 : 일본어에 필요한 각 글꼴에 대해 TMP 폰트어셋에 대한 하나의 Assetbundle).
- 부트 스트랩 단계에서 기본 Assetbundle 로드
- 로케일에 따라 대체 글꼴로 필요한 에셋번들을 로드합니다.
- 기본 AssetBundle의 각 폰트에 로컬라이즈된 폰트AssetBundle로 fallback 폰트를 할당합니다.
이미지를 사용하지 않는 경우 Default Sprite Asset 참조를 TMP 설정에서 제거하여 추가 메모리를 절약 할 수도 있습니다.
Best Fit 과 성능
TextMesh Pro에 다이나믹폰트기능이 없기 때문에 UGUI UIText 섹션에서 Best Fit과 관련된 위에서 설명한 문제는 발생하지 않습니다. TextMesh Pro 구성 요소에서 Bestfit을 사용할 때 고려해야 할 유일한 사항은 이진검색을 사용하여 올바른 크기를 찾는 것입니다. 텍스트 자동 크기 조정을 사용할 때 가장 길거나 가장 큰 텍스트 블록의 최적 포인트 크기를 테스트하는 것이 가장 좋습니다. 이 최적 크기가 결정되면 지정된 텍스트 개체에서 자동 크기 조정을 비활성화 한 다음 다른 텍스트 개체에서이 최적의 포인트 크기를 수동으로 설정합니다. 이는 성능을 향상시키는 이점이 있으며 시각적으로 좋지 않은 것으로 간주되는 다른 포인트 크기를 사용하는 텍스트 개체 그룹을 피할 수 있습니다.
Scroll Views
fill-rate 문제 다음으로 Unity UI의 스크롤 뷰는 런타임 성능 문제의 두 번째로 흔한 원인입니다. 스크롤 뷰는 일반적으로 콘텐츠를 표현하기 위해 많은 수의 UI 요소가 필요합니다. 스크롤보기를 채우는 두 가지 기본 접근 방식이 있습니다.
- 스크롤 뷰의 모든 내용을 표현하는 데 필요한 모든 요소로 채웁니다.
- 요소를 풀링하여 표시되는 콘텐츠를 나타내기 위해 필요에 따라 재배치합니다.
이 두 솔루션 모두 문제가 있습니다.
첫 번째 솔루션은 표시할 항목수가 증가함에 따라 모든 UI 요소를 인스턴스화하는 데 더 많은 시간이 필요하고 스크롤뷰를 다시 작성하는 데 필요한 시간도 늘어납니다. 소수의 Text 구성 요소 만 표시해야하는 스크롤뷰와 같이 스크롤뷰 내에 필요한 요소 수가 적은 경우이 방법은 단순성 때문에 선호됩니다.
두 번째 솔루션은 현재 UI 및 레이아웃 시스템에서 올바르게 구현하기 위해 상당한 양의 코드가 필요합니다. 두 가지 가능한 방법은 아래에서 자세히 설명합니다. 상당히 복잡한 스크롤 UI의 경우 일반적으로 성능 문제를 방지하기 위해 일종의 풀링 접근 방식이 필요합니다.
이러한 문제에도 불구하고 RectMask2D 컴포넌트를 Scroll View에 추가하여 모든 접근 방식을 개선 할 수 있습니다. 이 컴포넌트는 Scroll View의 뷰포트 외부에있는 Scroll View element가 Canvas를 다시 빌드 할 때 지오메트리를 생성, 정렬 및 분석해야하는 드로어블 element 목록에 포함되지 않도록합니다
Simple Scroll View element pooling
Unity에 내장 된 Scroll View 구성 요소를 사용하는 기본 편의성을 그대로 유지하면서 Scroll View로 개체 풀링을 구현하는 가장 간단한 방법은 하이브리드 접근 방식을 취하는 것입니다.
UI에 요소를 배치하여 레이아웃 시스템이 Scroll View의 콘텐츠 크기를 올바르게 계산하고 스크롤바가 제대로 작동하도록하려면 레이아웃 Element 컴포넌트있는 GameObject를 보이는 UI element에 대한 "placeholders"로 사용합니다.
그런 다음 Scroll View의 표시 영역을 채우는 데 충분한 표시 UI element 풀을 인스턴스화하고 이러한 element를 위치지정 자리표시자(positioning placeholders)의 부모로 지정합니다. Scroll View가 스크롤 될 때 UI 요소를 재사용하여 스크롤된 콘텐츠를 표시합니다.
batch비용은 Rect Transform이 아닌 Canvas 내의 Canvas Renderer 수에 따라 증가하기 때문에 일괄 처리해야하는 UI 요소의 수를 크게 줄일 수 있습니다.
Problems with the simple approach
현재 UI 요소가 다시 부모가되거나 형제 순서가 변경 될 때마다 해당 요소와 모든 하위 요소는 "더티"로 표시되고 캔버스를 강제로 다시 빌드합니다.
그 이유는 Unity가 트랜스 폼의 부모를 다시 지정하고 형제 순서를 변경하기 위해 콜백을 분리하지 않았기 때문입니다. 이 두 이벤트 모두 OnTransformParentChanged 콜백을 실행합니다. Unity UI의 Graphic 클래스 소스 (소스의 Graphic.cs 참조)에서 해당 콜백이 구현되고 SetAllDirty 메소드를 호출합니다 . 그래픽을 더럽히면 시스템은 다음 프레임이 렌더링되기 전에 그래픽이 레이아웃과 vertex을 다시 빌드하도록합니다.
스크롤 뷰 내 각 요소의 루트 RectTransform에 캔버스를 할당 할 수 있습니다. 그러면 스크롤 뷰의 전체 내용이 아닌 상위 요소 만 재 빌드 할 수 있습니다. 그러나 이로 인해 Scroll View를 렌더링하는 데 필요한 드로 콜 수가 증가하는 경향이 있습니다. 또한 Scroll View 내의 개별 요소가 복잡하고 12 개 이상의 그래픽 구성 요소로 구성된 경우, 특히 각 요소에 상당한 수의 레이아웃 구성 요소가있는 경우, rebuild 비용은 종종 저가형 장치의 프레임 속도를 눈에 띄게 줄일 수있을만큼 높습니다.
Scroll View UI 요소에 가변 크기가없는 경우 레이아웃 및 vertex을 완전히 다시 계산할 필요가 없습니다. 그러나 이 동작을 피하려면 상위 또는 형제 순서 변경 대신 위치 변경을 기반으로하는 개체 풀링 솔루션을 구현해야합니다.
위치 기반 스크롤 뷰 풀
위에서 설명한 문제를 피하기 위해 포함 된 UI 요소의 RectTransforms를 이동하여 개체를 풀링하는 Scroll View를 만들 수 있습니다. 이렇게하면 크기가 변경되지 않은 경우 이동 된 RectTransforms의 내용을 다시 빌드 할 필요가 없으므로 Scroll View의 성능이 크게 향상됩니다.
이를 수행하려면 일반적으로 Scroll View의 사용자 지정 하위 클래스를 작성하거나 사용자 지정 레이아웃 그룹 구성 요소를 작성하는 것이 가장 좋습니다. 후자는 일반적으로 더 간단한 솔루션이며 Unity UI의 LayoutGroup 추상 기본 클래스 의 하위 클래스를 구현하여 수행 할 수 있습니다 .
커스텀 레이아웃 그룹은 기본 소스 데이터를 분석하여 표시해야하는 데이터 요소 수를 조사하고 Scroll View의 Content RectTransform 크기를 적절하게 조정할 수 있습니다. 그런 다음 Scroll View 변경 이벤트 를 구독 하고이를 사용하여 표시되는 요소의 위치를 적절하게 조정할 수 있습니다.
6. Other UI Optimization Techniques and Tips
때로는 UI를 최적화하는 깨끗한 방법이 없습니다. 이 섹션에는 UI 성능을 개선하는 데 도움이 될 수있는 몇 가지 제안이 포함되어 있지만 일부는 구조적으로 "불분명"하거나 유지 관리가 어렵거나보기 흉한 부작용이있을 수 있습니다. 다른 것들은 초기 개발을 단순화하기위한 UI의 동작에 대한 해결 방법 일 수 있지만 성능 문제를 비교적 간단하게 만들 수도 있습니다.
RectTransform 기반 레이아웃
레이아웃 구성 요소는 더티로 표시 될 때마다 자식 요소의 크기와 위치를 다시 계산해야하므로 상대적으로 비용이 많이 듭니다. (자세한 내용은 기초 단계의 그래픽 재구성 섹션을 참조하십시오.) 주어진 Layout 내에 상대적으로 적은 수의 고정 된 요소가 있고 Layout이 비교적 단순한 구조를 가지고 있다면 Layout을 RectTransform 기반 레이아웃으로 대체 할 수 있습니다.
RectTransform의 앵커를 할당하여 RectTransform의 위치와 크기를 부모에 따라 크기를 조정할 수 있습니다. 예를 들어, 간단한 2 열 레이아웃은 두 개의 RectTransforms로 얻을 수 있습니다.
- 왼쪽 열의 앵커는 X : (0, 0.5) 및 Y : (0, 1)이어야합니다.
- 오른쪽 열의 앵커는 X : (0.5, 1) 및 Y : (0, 1)이어야합니다.
RectTransform의 크기와 위치에 대한 계산은 Transform 시스템 자체에 의해 네이티브 코드로 구동됩니다. 이것은 일반적으로 레이아웃 시스템에 의존하는 것보다 더 성능이 좋습니다. RectTransform 기반 레이아웃을 설정하는 MonoBehaviours를 작성할 수도 있습니다. 그러나 이것은 비교적 복잡한 작업이며이 가이드의 범위를 벗어납니다.
캔버스 비활성화
UI의 개별 부분을 표시하거나 숨길 때 UI 루트에서 GameObject를 활성화하거나 비활성화하는 것이 일반적입니다. 이렇게하면 비활성화 된 UI의 구성 요소가 입력 또는 Unity 콜백을 수신하지 않습니다.
그러나 이로 인해 Canvas는 VBO 데이터를 버립니다. 캔버스를 다시 활성화하려면 다시 빌드 및 다시 배치 프로세스를 실행하기 위해 캔버스 (및 모든 하위 캔버스)가 필요합니다. 이 문제가 자주 발생하면 CPU 사용량이 증가하여 응용 프로그램의 프레임 속도가 끊길 수 있습니다.
해결 방법은 표시/숨길 UI를 자체 Canvas 또는 하위캔버스에 배치한 다음이 개체에서 Canvas 구성 요소를 활성화 / 비활성화하는 것입니다.
이로 인해 UI의 메시가 그려지지 않지만 메모리에 상주하고 원래의 일괄 처리가 유지됩니다. 또한 UI 계층에서 OnEnable 또는 OnDisable 콜백이 호출 되지 않습니다 . 그러나 이렇게하면 숨겨진 UI 내에서 MonoBehaviour가 비활성화되지 않으므로 이러한 MonoBehaviour는 Update와 같은 Unity 라이프 사이클 콜백을 계속 수신합니다.
이 문제를 피하기 위해 이러한 방식으로 비활성화되는 UI의 MonoBehaviour는 Unity의 라이프 사이클 콜백을 직접 구현해서는 안되며 대신 UI의 루트 게임 오브젝트에있는 "콜백 관리자"MonoBehaviour에서 콜백을 받아야합니다. 이 "콜백 관리자"는 UI가 표시/숨겨 질 때마다 알림을 받을 수 있으며 필요에 따라 수명주기 이벤트가 전파되거나 전파되지 않도록 할 수 있습니다.
Event Cameras 할당
worldspace 또는 screen spacee-camera모드 에서 렌더링하도록 설정된 캔버스와 함께 Unity의 내장 입력 관리자를 사용하는 경우 항상 이벤트 카메라 또는 카메라 렌더링 속성을 각각 설정하는 것이 중요합니다. 스크립트에서 이것은 항상 worldCamera 속성으로 노출됩니다.
이 속성이 설정되지 않은 경우 Unity UI는 MainCamera 태그가있는 게임 오브젝트에 연결된 카메라 구성 요소를 찾아서 메인 카메라를 검색합니다. 이 조회는 World Space 또는 CameraSpaceCanvas 당 최소 한 번 발생합니다. GameObject.FindWithTag가 느린 것으로 알려져 있으므로 모든 World Space 및 Camera Space 캔버스에 디자인 타임 또는 초기화 시점에 미리 할당 된 Camera 속성을 갖는 것이 좋습니다.
오버레이 캔버스에는이 문제가 발생하지 않습니다.
'Unity > 최적화' 카테고리의 다른 글
- Total
- Today
- Yesterday