Unity 검색

Unity UI에 관한 최고의 최적화 팁

최근 업데이트: 2019년 1월

이 페이지의 내용: 캔버스 분할, Camera.main 및 레이아웃 그룹 사용 방지, UI 오브젝트의 스마트 풀링 등을 비롯하여 콘텐츠에 최적화된 UI 요소를 만드는 방법에 대한 팁입니다.

유니티 엔지니어 이안 던도어(Ian Dundore)가 진행한 유니티 털어보기: 성능 향상을 위한 팁(Squeezing Unity: Tips for raising performance) 세션(Unity UI에 관한 섹션은 23분 38초에 시작)을 시청하면서 이러한 팁을 더 많이 알아보세요.

캔버스 분할

문제: UI 캔버스에서 한두 가지 요소를 변경하면 캔버스 전체가 변경된 것으로 인식됩니다.

캔버스는 Unity UI의 기본 컴포넌트입니다. 캔버스에 배치된 UI 요소를 나타내는 메시를 생성하고, UI 요소가 변경되면 메시를 다시 생성하며, GPU에 드로우 콜을 발행하여 UI가 실제로 표시되도록 합니다.

이러한 메시를 생성하는 데는 많은 자원이 소모될 수 있습니다. UI 요소를 배치로 수집하여 드로우 콜 횟수를 가능한 한 줄여야 합니다. 배치 생성에는 많은 자원이 소모되므로 필요한 경우에만 재생성하는 것이 좋습니다. 여기서 문제는 캔버스에서 하나 이상의 요소가 변경되면 전체 캔버스를 다시 분석하여 요소를 드로우하는 최적의 방식을 파악해야 한다는 점입니다.

많은 사용자들은 하나의 캔버스에 수천 가지 요소를 사용하여 게임 전체의 UI를 빌드합니다. 때문에 하나의 요소를 변경하면 CPU 사용량이 급격하게 증가하여 수 밀리초가 소요될 수 있습니다(리빌드가 많은 자원을 소모하는 이유를 알아보려면 이안의 발표 중 24분 55초 부분을 시청하세요).

솔루션: 캔버스를 분할합니다.

각 캔버스는 포함된 요소를 다른 캔버스의 요소로부터 분리하는 섬과 같습니다. 따라서, 기본 툴에서 캔버스를 분할하면 유니티 UI의 배칭 문제를 해결할 수 있습니다.

캔버스를 중첩할 수도 있습니다. 그러면 디자이너가 여러 캔버스에 걸쳐 다양한 요소가 화면의 어느 위치에 표시되는지 고민할 필요 없이 대규모의 계층적 UI를 생성할 수 있습니다. 자식 캔버스도 부모 캔버스와 형제 캔버스 모두로부터 콘텐츠를 분리할 수 있으며, 자체적으로 지오메트리를 유지하고 자체 배칭을 수행합니다.

캔버스를 자식 캔버스로 더 세분화하는 경우, 동적 요소를 정적 요소와 분리하는 등 업데이트 시점에 따라 요소를 그룹화하는 것이 좋습니다. 비디오의 29분 36초 부분에 이안이 스마트한 캔버스 세분화의 좋은 예를 설명합니다.

최적의 그래픽 레이캐스터(Graphic Raycaster) 사용

문제: 최적의 그래픽 레이캐스터 사용법

그래픽 레이캐스터(Graphic Raycaster)는 입력 사항을 UI 이벤트로 변환하는 컴포넌트로, 화면/터치 입력을 이벤트로 변환한 다음 관련 UI 요소로 전송합니다. 하위 캔버스를 비롯하여 입력이 필요한 모든 캔버스에 그래픽 레이캐스터가 있어야 합니다.

이름과는 달리 그래픽 레이캐스터는 실제로 레이캐스터가 아니며, 기본적으로 UI 그래픽만 테스트합니다. 그래픽 레이캐스터는 특정 캔버스에 입력을 수신하는 UI 요소를 포착하고 교차 지점을 확인합니다. 즉, 그래픽 레이캐스터 캔버스에 포함된 각 UI 요소의 RectTransform에 대해 입력 이벤트가 발생하는 지점이 인터랙티브로 표시되는지 확인합니다.

여기서 일부 UI 요소의 경우 업데이트 수신과 관련이 없다는 점이 문제가 됩니다.

솔루션: 정적 요소나 비인터랙티브 요소에 대한 레이캐스트 타겟을 비활성화합니다.

텍스트 또는 버튼이 이러한 요소에 해당합니다. 레이캐스트 타겟을 비활성화하면 그래픽 레이캐스터가 각 프레임에 대해 수행해야 하는 교차 지점 검사 횟수가 즉시 감소합니다.

Unity UI 최적화 팁

문제: 그래픽 레이캐스터가 어떤 측면에서는 레이캐스터의 역할을 합니다.

캔버스에서 렌더 모드를 월드 공간 카메라나 화면 공간 카메라로 설정하는 경우 차단 마스크를 설정할 수도 있습니다. 차단 마스크는 레이캐스터가 2D 또는 3D 물리를 통해 레이캐스트를 수행할지 결정하여 일부 물리 오브젝트가 사용자와 UI 간의 상호작용을 차단하는지 확인합니다.

솔루션: 2D 또는 3D 물리를 통한 레이캐스트는 많은 자원을 소모하므로 최대한 적게 사용합니다.

또한, 인터랙티브 이벤트를 확인할 필요가 없는 비인터랙티브 UI 캔버스에는 그래픽 레이캐스터를 추가하지 않음으로써 그래픽 레이캐스터의 수를 최소화하세요.

Camera.main 사용 지양

문제: 상호작용 이벤트를 전송한 카메라 정보가 월드 공간(World Space) 캔버스에 제공되어야 함

월드 공간 또는 카메라의 스크린 공간에서 렌더링하도록 캔버스를 설정하는 경우, UI의 그래픽 레이캐스터를 위한 상호작용 이벤트를 생성하는 데 사용할 카메라를 지정할 수 있습니다. 이 설정은 “스크린 공간 - 카메라” 캔버스에 필요하며, "렌더 카메라"라고 불립니다.

Unity UI 최적화를 위한 팁 스크린 공간 카메라

그러나 “월드 공간” 캔버스의 경우 이 설정은 선택 사항이며, "이벤트 카메라(Event Camera)"라고 불립니다.

Unity UI 최적화를 위한 팁 월드 공간

월드 공간 캔버스의 이벤트 카메라 필드를 비워 두는 경우에도 캔버스는 게임의 기본 카메라를 사용하여 이벤트를 수신하며, 어느 카메라가 기본 카메라인지 파악하기 위해 카메라 기본 속성에 액세스합니다.

Unity UI 최적화를 위한 팁 카메라 메인 프로퍼티

유니티가 사용하는 코드 경로에 따라 캔버스는 프레임당, 그래픽 레이캐스터당, 월드 공간 캔버스당 Camera.main에 7~10회 접속합니다. Camera.main은 액세스될 때마다 Object.FindObjectWithTag를 호출하므로 이로 인해 런타임이 지연될 수 있습니다.

솔루션: Camera.main 사용을 지양합니다.

카메라 레퍼런스를 캐싱하고 기본 카메라를 추적하는 시스템을 구축하세요. 월드 공간 캔버스를 사용하는 경우 항상 이벤트 카메라를 할당하세요. 이 설정을 절대 비워두지 마세요. 이벤트 카메라를 변경해야 하는 경우 이벤트 카메라 프로퍼티를 업데이트하는 코드를 작성하세요.

가능한 한 레이아웃 그룹 지양

문제: GetComponents 호출을 최소 1회 수행하여 레이아웃을 변경하려고 하는 모든 UI 요소.

레이아웃 시스템에서 하나 이상의 자식 요소가 변경되면 레이아웃 시스템이 변경된 것으로 인식됩니다. 변경된 자식 요소는 해당 자식 요소를 가지는 레이아웃 시스템을 무효화시킵니다.

레이아웃 시스템 간략 정보: 레이아웃 시스템은 레이아웃 요소 바로 위에 있는 인접한 레이아웃 그룹입니다. 레이아웃 요소 컴포넌트뿐만 아니라 UI 이미지, 텍스트, 사각 스크롤 영역 역시 레이아웃 요소에 해당됩니다. 또한, 사각 스크롤 영역은 레이아웃 그룹에도 해당됩니다.

문제로 다시 돌아가서, 레이아웃을 변경하려고 하는 각 UI 요소는 최소 1회의 GetComponents 호출을 수행합니다. 이 호출은 레이아웃 요소의 부모 수준에서 유효한 레이아웃 그룹을 찾습니다. 유효한 레이아웃 그룹을 찾으면 더 이상 레이아웃 그룹을 찾을 수 없거나 계층 구조의 루트에 도달할 때까지(어느 쪽이든 먼저 발생할 때까지) 변환 계층 구조 상위로 계속 진행합니다. 따라서 각 레이아웃 그룹은 각 자식 레이아웃 요소의 변경 프로세스에 1회의 GetComponents 호출을 더함으로써 중첩된 레이아웃 그룹의 성능을 극도로 저하시킵니다.

솔루션: 가능한 한 레이아웃 그룹을 지양합니다.

비례 레이아웃의 경우 앵커를 사용하세요. 요소 개수가 변화하는 핫 UI의 경우 레이아웃을 계산하는 자체 코드를 작성하되 변경 사항이 발생할 때마다 사용하지 말고 필요한 경우에만 사용하도록 하세요.

스마트한 방식으로 UI 오브젝트 풀링

문제: UI 오브젝트를 잘못된 방식으로 풀링합니다.

대부분의 경우 UI 오브젝트의 부모 수준을 변경한 다음 비활성화하여 풀링하지만 이러한 방식은 불필요한 변경을 초래합니다.

솔루션: 먼저 오브젝트를 비활성화한 다음, 부모 수준을 풀로 변경합니다.

그러면 기존의 계층 구조를 1회 변경하게 되지만, 부모 수준을 바꿀 때에는 기존 계층 구조를 다시 변경하지 않으며, 새로운 계층 구조는 전혀 변경하지 않게 됩니다. 풀에서 오브젝트를 제거할 때에는 먼저 부모 수준을 바꾼 다음 데이터를 업데이트하고 활성화하세요.

캔버스를 숨기는 방법

문제: 캔버스를 숨기는 방법

일부 UI 요소와 캔버스를 최대한 효율적으로 숨기는 방법은 무엇일까요?

솔루션: 캔버스 컴포넌트 자체를 비활성화합니다.

캔버스 컴포넌트를 비활성화하면 캔버스가 더 이상 GPU에 드로우 콜을 발행하지 않으므로 캔버스가 보이지 않게 됩니다. 하지만 캔버스는 버텍스 버퍼를 폐기하지 않으며 모든 메시와 버텍스를 유지하므로, 캔버스를 다시 활성화하면 리빌드를 트리거하지 않고 다시 드로우하게 됩니다.

또한 캔버스 컴포넌트를 비활성화하더라도 캔버스의 계층 구조에서 많은 자원을 소모하는 OnDisable/OnEnable 콜백이 트리거되지 않습니다. 단, 많은 자원을 소모하는 프레임당 코드를 실행하는 하위 컴포넌트를 반드시 비활성화하세요.

UI 요소에 애니메이터를 사용하는 최적의 방식

문제: UI에서 애니메이터 사용

애니메이터는 애니메이션의 값이 변경되지 않더라도 모든 프레임의 요소를 변경하며, 무연산 검사 기능이 없습니다.

솔루션:

항상 변화하는 동적 요소에만 애니메이터를 적용하세요. 변화 빈도가 낮거나 이벤트에 대한 반응으로만 짧은 시간 동안 변화하는 요소의 경우 자체 코드를 작성하거나 트위닝 시스템을 구축하세요(에셋 스토어에서 많은 리소스 확인).

리소스 더 보기
확인

유니티에서는 웹 사이트의 모든 기능을 최대로 이용할 수 있도록 쿠키를 사용합니다. 자세한 정보는 쿠키 정책 페이지를 참조하세요.