Unity 검색

Unity Asset Bundles
Unity Asset Bundles
공유

Is this article helpful for you?

Thank you for your feedback!

에셋 번들은 게임에 사용되는 에셋을 담은 아카이브 파일입니다. 에셋 번들을 사용하면 게임을 논리적 블록으로 분할할 수 있어서 게임 빌드의 크기를 줄이면서 온디맨드 방식으로 콘텐츠를 제공하고 업데이트할 수 있습니다. 게임의 패치나 DLC를 제공하는 경우에도 흔하게 사용됩니다. 에셋 번들은 프리팹, 머티리얼, 텍스처, 오디오 클립, 씬을 비롯한 모든 종류의 에셋을 포함할 수 있지만 스크립트는 포함할 수 없습니다.

예전에는 에셋 번들을 수동으로 만들고 각 에셋을 번들에 따라 표시한 후에 런타임에 종속 관계를 직접 트래킹하고 해결해야 했습니다. 지금은 사용자가 정의한 에셋 그룹을 기반으로 에셋 번들을 만들고 종속 관계를 투명하게 로드 및 처리하는 어드레서블 시스템에서 모든 작업을 처리합니다.

에셋 번들의 작동 방식을 설명하는 가이드는 많습니다. 그러므로 이번 게시물에서는 에셋 번들의 잘 알려지지 않은 측면을 다루면서 게임 성능, 메모리 런타임 사용량, 전반적인 호환성에 집중하려 합니다.

에셋 로드 및 언로드

번들에 포함된 에셋을 사용하려고 할 때마다 Unity는 우선 해당 번들을 메모리에 로드한 후에 해당 에셋을 메모리에서 로드합니다.

에셋 번들에 있는 특정 에셋을 부분적으로 로드할 수는 있지만 그 반대의 경우는 불가능합니다. 다시 말해 에셋 번들 내의 에셋이 로드되면 전체 에셋 그룹이 더 이상 필요하지 않은 경우에만 언로드할 수 있습니다.

따라서 번들 구조가 적절하지 않다면 게임이 진행됨에 따라 런타임 메모리 사용량이 증가하여 성능이 저하되고 크래시가 발생할 수 있습니다. 그러므로 번들에 많은 에셋을 담는 것은 지양해야 합니다. 런타임 메모리를 많이 차지하게 되어 게임에 병목 현상을 유발할 수 있기 때문입니다. 대신 에셋의 로드 및 사용 빈도에 따라 에셋 번들을 패킹하는 것이 바람직합니다.

엔진 버전 호환성

에셋 번들은 보통 상위 호환성을 지니므로 이전 버전의 Unity로 제작된 번들은 대부분 최신 버전 Unity로 제작된 게임에서도 작동합니다(TypeTree 정보를 제거하지 않은 경우이며 뒤에서 설명할 예정). 그 반대의 경우는 성립하지 않습니다. 게임 빌드에 사용된 버전보다 최신 버전의 Unity로 제작된 번들은 제대로 로드되지 않을 가능성이 높습니다.

번들의 버전과 게임 빌드에 사용되는 엔진의 버전이 큰 차이를 보일수록 호환성도 떨어집니다. 번들 자체는 로드되더라도 오브젝트 직렬화 방식이 변경되어 번들에 포함된 오브젝트가 새로운 버전의 Unity에서 올바르게 로드되지 않아 문제가 발생하는 경우도 있습니다. 이 경우에는 번들을 다시 제작하여 호환성을 유지해야 합니다.

다른 Unity 버전에서 만든 번들을 로드하면 성능 비용이 발생하기도 합니다. 이 내용은 아래의 TypeTree 섹션에서 다룰 것입니다.

따라서 게임 빌드의 Unity 버전을 업데이트할 때마다 기존 에셋 번들을 철저히 테스트하고 가능할 때마다 업데이트하는 것이 좋습니다.

크로스 플랫폼 호환성

에셋 번들은 보통 크로스 플랫폼을 지원하지 않습니다. 에디터에서 다른 타겟 플랫폼의 번들을 로드할 수는 있어도 디바이스에서는 해당 번들이 로드되지 않습니다.

플랫폼별로 특화되지 않은 에셋이 포함된 번들도 마찬가지입니다.

이러한 제약이 있는 이유는 타겟 플랫폼에서만 적용되는 방식으로 데이터가 최적화되거나 압축될 수 있기 때문입니다. 다른 플랫폼과 공유하면 안 되는 플랫폼별 데이터가 번들에 포함되어 있을 수도 있습니다. 따라서 이러한 제약을 통해 다른 플랫폼에 적합하지 않은 콘텐츠가 유출되는 것을 방지할 수 있습니다.

로딩 캐시

로딩 캐시는 Unity가 에셋 번들을 위해 최근에 액세스한 데이터를 저장하는 공유 페이지 풀입니다. 전역 캐시이므로 게임 내 모든 에셋 번들에서 공유됩니다.

이는 최근에 도입된 기능이며 Unity 2021.3에 도입된 다음 2019.4 버전으로 백포트되었습니다. 예전에는 Unity가 각 에셋 번들에 별도의 캐시를 사용함에 따라 런타임 메모리 사용량이 상당히 높았습니다(아래의 ‘직렬화된 파일 버퍼’ 섹션에서 설명).

캐시의 기본 설정 크기는 1MB지만 AssetBundle.memoryBudgetKB를 설정하여 변경할 수 있습니다.

대부분의 경우 기본 캐시 크기로도 충분하지만 크기를 변경하면 게임에 유리한 경우도 있습니다. 예를 들어 작은 오브젝트가 많이 포함된 번들의 경우 캐시 크기를 늘리면 캐시 히트가 증가하여 게임 성능이 향상될 수 있습니다.

추가 내부 데이터

에셋 번들에는 Unity가 어떤 에셋을 어떻게 로드할지 파악하기 위해 사용되는 여러 추가 정보 및 헤더와 전용 캐시(사용 중인 Unity 버전에 따라 다름)가 게임 에셋과 함께 포함되어 있습니다.

목차

번들에 포함된 에셋의 맵입니다. 목차를 사용하면 번들에 있는 개별 에셋을 이름별로 룩업하고 로드할 수 있습니다. 수천 개의 오브젝트가 포함된 매우 큰 규모의 에셋 번들이 아니라면 목차가 점유하는 메모리 크기는 보통 문제가 되지 않습니다.

사전 로드 테이블

사전 로드 테이블은 번들에 포함된 각 에셋의 종속 관계를 나열합니다. Unity는 이 테이블을 사용해 에셋을 정확하게 로드하고 구성합니다.

번들에 포함된 에셋에 명시적 및 암시적 종속 관계가 많고 다른 번들에서 비롯된 연쇄적 종속 관계도 있는 경우에는 사전 로드 테이블이 상당히 커질 수 있습니다. 이러한 이유로 번들을 디자인할 때는 종속 관계 체인을 최소화하는 것이 좋습니다.

TypeTree

TypeTree는 에셋 번들에 포함된 오브젝트의 직렬화된 레이아웃을 정의합니다.

TypeTree 크기는 번들 내에 포함된 오브젝트 유형의 개수에 좌우됩니다. 따라서 다양한 유형의 오브젝트를 섞어 대규모 번들을 만드는 방식은 지양해야 합니다.

TypeTree는 이전 버전의 엔진에서 만든 에셋 번들을 계속 로드하며 게임 빌드의 Unity 버전을 업그레이드할 때 호환성을 유지하기 위해 필요합니다. 예를 들어 오브젝트의 포맷이나 구조가 변경된 경우에도 TypeTree를 사용하면 Unity에서 해당 오브젝트를 로드하도록 안전한 바이너리(Safe Binary) 읽기를 수행할 수 있습니다. 이 방법은 성능 비용이 발생하므로 엔진을 업데이트할 때는 가능하면 번들을 업데이트하는 것이 바람직합니다.

번들을 제작할 때 BuildAssetBundleOptions.DisableWriteTypeTree 플래그를 설정하면 TypeTree를 비활성화할 수 있습니다. 그러면 번들 및 관련 메모리 오버헤드가 줄어듭니다. 하지만 게임 빌드의 엔진 버전을 업데이트할 때마다 모든 번들을 다시 제작해야 합니다. 사용자 생성 콘텐츠로 플레이어가 제작한 번들에 의존하는 경우 이러한 점이 특히 문제가 될 수 있으니 불가피한 경우가 아니라면 TypeTree를 계속 활성화하는 편이 좋습니다.

TypeTree를 비활성화해도 안전한 경우는 게임 빌드 자체에 번들이 포함되었을 때입니다. 이러한 상황에서 엔진을 업그레이드하려면 어차피 새 게임 빌드와 새 에셋 번들을 만들어야 하므로 하위 호환성 측면을 신경 쓰지 않아도 됩니다.

각 번들에는 고유한 TypeTree가 있으므로 같은 유형의 오브젝트를 포함하는 작은 번들을 여러 개 사용하면 디스크에서 차지하는 총 크기가 약간 증가합니다. 로드할 때는 TypeTree가 메모리의 전역 캐시에 저장되므로 다수의 에셋 번들에 같은 유형의 오브젝트를 저장한다고 런타임 메모리 비용이 더 많이 발생하지는 않습니다.

직렬화된 파일 버퍼

참고:이 기능은 위 설명대로 Unity 2019.4부터 전역 공유 로딩 캐시로 대체되었습니다.

에셋 번들이 로드되면 Unity는 내부 버퍼를 할당하여 직렬화된 파일을 메모리에 저장합니다.

일반 에셋 번들이 하나의 직렬화된 파일을 포함하는 반면, 스트리밍 씬 에셋 번들은 해당 번들에 포함된 씬마다 최대 2개의 파일을 포함합니다. 버퍼의 크기는 플랫폼에 따라 다릅니다. Switch, PlayStation, Windows RT의 버퍼는 128KB지만 다른 모든 플랫폼에서는 14KB의 버퍼가 제공됩니다.

따라서 매우 작은 에셋 번들을 다량으로 사용하지 않는 것이 좋습니다. 제공되는 실제 에셋에 비해 버퍼가 차지하는 메모리가 클 수 있기 때문입니다.

CRC 무결성 검사

CRC(순환 중복 검사)는 에셋 번들의 체크섬을 확인하여 예상과 정확히 일치하는 콘텐츠가 게임에 제공되도록 합니다. CRC는 번들의 압축되지 않은 콘텐츠를 기준으로 계산됩니다.

콘솔에서 에셋 번들은 로컬 스토리지에 타이틀과 함께 설치되거나 DLC로 다운로드되므로 CRC 검사를 수행할 필요가 없습니다. PC나 모바일 등의 다른 플랫폼에서는 CDN에서 다운로드한 번들에 CRC 검사를 수행하는 것이 중요합니다. 그래야 파일이 손상되거나 잘리는 것을 방지하여 잠재적 크래시의 발생을 막을 수 있고 변조가 발생할 가능성도 차단할 수 있습니다.

CRC 검사는 CPU 사용 측면에서 상당한 리소스가 소요되며 콘솔과 모바일에서는 특히 더 그렇습니다. 따라서 CRC 검사를 로컬 및 캐시된 번들에서는 비활성화하고 캐시되지 않은 원격 번들에서만 활성화하는 방법이 대신 사용됩니다.

에셋 룩업 시 오버헤드 감소

Unity는 번들 내에서 에셋을 룩업하는 세 가지 방법을 제공합니다.

  • 프로젝트 상대 경로(Assets/Prefabs/Characters/Hero.prefab)
  • 에셋 파일 이름(Hero)
  • 확장자를 포함한 에셋 파일 이름(Hero.prefab)

편리한 방법이기는 하지만 비용이 발생합니다. 마지막 두 가지 방법을 지원하려면 Unity에서 룩업 테이블을 만들어야 합니다. 이때 규모가 큰 번들이라면 상당한 양의 메모리가 소요될 수 있습니다.

또한 프로젝트 상대 경로가 아닌 다른 방법을 사용하여 에셋을 로드하면 테이블 룩업이 필요하기 때문에 성능 비용이 발생합니다.

따라서 두 방법은 사용하지 않는 것이 좋습니다. 에셋 번들을 제작할 때 두 방법을 비활성화할 수도 있습니다. 그러면 에셋 번들의 로딩 성능과 런타임 메모리 사용량이 개선됩니다.

이를 위해서는 번들을 제작할 때 아래 두 플래그를 설정하면 됩니다.

에셋 관리에 대해 자세히 알아보고 피드백을 공유하거나 커뮤니티 및 유니티 직원과 소통하려면 에셋 관리 포럼을 확인하세요. 

2024년 4월 16일 엔진 & 플랫폼 | 8 분 소요

Is this article helpful for you?

Thank you for your feedback!

관련 게시물