원문 : AssetBundle Usage Patterns
주의 : 번역이 개판이므로 이상하면 원문을 참조하십시오.
주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.
주의 : 아래쪽의 일부 섹션들은 현재 제 관심사와 관계가 없어 보여서 번역하지 않았습니다.
AssetBundle Usage Patterns
확인 완료한 버전: 5.3 - 난이도: 고급
이 문서는 유니티 5 의 애셋, 리소스, 리소스 관리를 다루는 기사 시리즈의 다섯 번째 챕터입니다.
이 시리즈의 이전 챕터에서는 애셋번들의 기초에 대해 다뤘는데, 특히 다양한 로딩 API 의 저수준 동작에 대해 다뤘습니다. 이 챕터는 애셋번들을 실무에서 사용하는 것의 다양한 관점에 대한 문제들과 잠재적 해결책들에 대해 논의합니다.
4.1. Managing Loaded Assets
메모리에 민감한 환경에서 로드된 오브젝트의 크기와 개수를 조심스럽게 제어하는 것은 매우 중요합니다. 유니티는 활성화된 씬에서 오브제트가 제거될 때 자동으로 그것들을 언로드하지 않습니다. 애셋 정리는 특정 시점에 발동되며, 이것은 수동으로 발동될 수도 있습니다.
애셋번들 자체는 매우 주의깊게 관리되어야만 합니다. ( 유니티 캐시에 있거나 AssetBundle.LoadFromFile 에 의해 로드된 ) 로컬 저장소로부터 로드된 애셋번들은 최소한의 메모리 부하를 가지는데, 약 10 ~ 40 KB 를 잘 넘지 않습니다. 이 부하는 매우 많은 개수의 애셋번들이 제출될 때 여전히 문제가 될 수 있습니다.
대부분의 프로젝트들은 유저에게 ( 레벨을 리플레이하는 것과 같은 ) 재경험 칸텐츠를 허용하며, 애셋번들을 로드하거나 언로드해야 하는 시점을 아는 것은 중욯바니다. 만약 애셋번들이 부적절하게 언로드되면, 그것은 메모리상에서 오브젝트가 중복되도록 만들 수 있습니다. 애셋번들을 부적절하게 언로드하는 것은 특정 환경에서 원치않는 동작을 유발할 수도 있는데, 이는 텍스쳐가 없어진다던가 하는 상황을 만듭니다. 이런 일이 왜 일어나는지 이해하려면, Asset, Objects, and Serialization 챕터의 Inter-Object references 섹션을 참고하십시오.
애셋과 애셋번들을 관리할 때 가장 중요한 것은 AssetBundle.Unload 의 인자를 true 로 공급하느냐 false 로 공급하느냐에 따라 다른 동작을 한다는 것을 이해하는 것입니다.
이 API 는 호출되고 있는 애셋번들의 헤더 정보를 언로드합니다. 인자는 이 애셋번들로부터 인스턴스화된 모든 오브젝트를 언로드할지 여부를 지정합니다. 만약 true 라면, 애셋번들로부터 생성된 모든 오브젝트들은 즉시 언로드됩니다 - 심지어 활성화된 씬에서 현재 사용중일지라도 말이죠.
예를 들어, 머티리얼 M 이 애셋번들 AB 로부터 로드되고, M 이 현재 활성화된 씬에서 사용주이라고 가정해 보겠습니다.
만약 AB.Unload(true) 가 호출되면, M 은 씬에서 제거되며 파괴되고 언로드될 것입니다. 그러나 AB.Unload(false) 가 호출되면 AB 의 헤더 정보가 언로드되지만, M 은 씬에 남아 있게 되고 여전히 자신의 기능을 수행할 것입니다. AssetBundle.Unload(false) 호출은 M 과 AB 간의 링크를 끊습니다. 만약 AB 가 나중에 다시 로드된다면, AB 에 포함된 오브젝트들에 대한 새로운 복사본이 메모리에 로드될 것입니다.
만약 AB 가 나중에 다시 로드된다면, 애셋번들의 헤더 정보의 새로운 복사본이 다시 로드될 것입니다. 하지만 M 은 AB 의 새 복사본으로부터 로드되지 않습니다. 유니티는 AB 와 M 의 새로운 복사본 사이의 어떠한 링크도 만들어 주지 않습니다.
만약 AB.LoadAsset() 을 호출해 M 을 다시 로드한다면, 유니티는 M 의 예전 복사본을 AB 안의 데이터의 인스턴스인 것처럼 해석하지 않을 것입니다. 그러므로 유니티는 M 의 새로운 복사본을 로드하고 씬에는 M 에 대한 동일한 두 개의 복사본이 존재하게 될 것입니다.
대부분의 프로젝트의 경우에, 이러한 동작은 원치않는 동작입니다. 대부분의 프로젝트들은 AssetBundle.Unload(true)를 사용해야 하며 오브젝트들이 중복되지 않는다고 확신하기 위한 기법을 써야 합니다. 두 개의 기법이 있습니다:
- 애플리케이션 생명주기 동안에 일시적인 애셋번들이 언로드되는 잘 정의된 지점을 가지고 있어야 합니다. 이러한 예로는 레벨이 전환되는 시점이나 로딩 스크린이 나오고 있는 동안이 있습니다. 이는 단순하고 가장 흔한 선택입니다.
- 개별 오브젝트들에 대한 참조 카운트를 유지하고, 애셋번들에서 생성된 오브젝트가 더 이상 사용되지 않을 때 애셋번들을 언로드하는 것입니다.
먄약 애플리케이션이 AssetBundle.Unload(false) 를 사용해야만 한다면, 개별 오브젝트들은 두 가지 방식으로 언로드될 수 있습니다:
- 원치않는 오브젝트에 대한 모든 참조를 제거하는데, 이는 씬과 코드에서 모두 이루어져야 합니다. 이 작업이 끝나면 Resources.UnloadUnusedAllsets 를 호출합니다.
- 씬을 non-additively 로 로드합니다. 이는 현재 씬에 있는 모든 오브젝트들을 파괴하고 Resources.UnloadUnusedAssets 를 자동으로 호출할 것입니다.
만약 프로젝트가 오브젝트가 로드되거나 언로드되는 동안 사용자로 하여금 대기하게 만들 수 있는( 게임 모드나 레벨이 전환되는 사이와 같은 ) 잘 정의된 지점을 가지고 있다면, 이 지점들은 필요한 만큼 많은 오브젝트들을 언로드하고 새로운 오브젝트들을 로드하는데 사용되어야 합니다.
이를 위한 가장 단순한 방법은 분리된 청크의 프로젝트를 씬에 패키징하는 것입니다. 그리고 나서 씬의 모든 종속성과 함께 씬을 애셋번들로 빌드합니다. 그리고 나서 애플리케이션은 씬 "로딩"에 들어 가는데, 이전 씬에 포함된 모든 애셋번들을 완전히 언로드하고, 새로운 씬을 포함하는 애셋번들을 로드합니다.
이것이 가장 단순한 플로우이기는 하지만, 어떤 프로젝트들은 좀 더 복잡한 애셋번들 관리를 요구합니다. 보편적인 애셋번들 설계 패턴이라는 것은 존재하지 않습니다. 각 프로즈게트의 데이터는 서로 다릅니다. 오브젝트들을 애셋번들로 그룹화하는 방법을 결정할 때, 일반적으로 가장 좋은 것은 동시에 로드되거나 업데이트되어야만 하는 오브젝트들을 애셋번들로 묶는 것입니다.
예를 들어, 롤플레잉 게임을 생각해 봅시다. 개별 맵과 컷씬들은 씬에 의해 애셋번들로 그룹화될 수 있습니다. 하지만 어떤 오븢게트들은 대부분의 씬에서 필요할 것입니다. 애셋번들은 초상화, 인게임 UI, 서로 다른 캐릭터 모델 및 텍스쳐를 제공하기 위해서 빌드도리 수 있습니다. 나중에 언급한 오브젝트들과 애셋들은 애플리케이션 시작시에 로드되어 애플리케이션 생명주기 동안 로드된 상태를 유지하는 애셋번들의 2차 집합으로 그룹화될 수 있습니다.
애셋번들이 언로드된 다음에 유니티가 애셋번들로부터 오브젝트를 다시 로드해야 하는 경우에 다른 문제가 발생할 수 있습니다. 이 경우에, 리로드는 실패할 것이며, 유니티 에디터의 계층에서는 (Missing) 오브젝트로 나타날 것입니다.
이 상황은, 모바일 앱이 중단되었을 때나 사용자가 PC 를 lock 했을 때 같이, 유니티가 그것의 그래픽스 칸텍스트에 대한 제어를 잃거나 다시 획득할 때 주로 발생합니다. 이 경우, 유니티는 GPU 에 텍스쳐와 쉐이더를 다시 업로드해야 합니다. 만약 이 애셋들을 위한 소스 애셋번들이 이용할 수 없는 상태라면, 애플리케이션은 "missing shader" 마젠타색으로 씬의 오븢게트를 렌더링하게 될 것입니다.
4.2. Distribution
프로젝트의 애셋번들을 클라이언트에 배포하기 위한 두 가지 기본 방식이 있습니다: 프로젝트와 함께 설치하는 것과 설치 후에 다운로드하는 것. 어떤 방식을 선택하느냐는 프로젝트가 실행되어야 하는 플랫폼의 기능과 제약에 의해 결정됩니다. 모바일 프로젝트들은 보통 초기 설치 크기를 줄이고 무선 다운로드 크기 제한 아래로 파일 크기를 유지하기 위해서 post-install 옵션을 선택합니다. 콘솔이나 PC 프로젝트들은 일반적으로 애셋번들을 초기 설치에 포함시킵니다.
적절한 구조를 만들면 애셋번들이 처음에 어떤 방식으로 전달되었느냐의 여부와는 관계없이 프로젝트의 post-install 로 새로운 혹은 수정된 칸텐트를 패치할 수 있습니다. 이와 관련한 더 많은 정보를 원한다면 이 기사의 Patching with AssetBundles 섹션을 참고하십시오.
4.2.1. Shipped with project
프로젝트에 애셋번들을 포함시키는 것은 애셋번들을 배포하는 가장 단순한 방법입니다. 왜냐하면 그것은 추가적인 다운로드 관리 코드를 필요로하지 않기 때문입니다. 프로젝트가 애셋번들을 설치 파일에 포함시켜야 하는 이유는 크게 두 가지가 있습니다:
- 프로젝트 빌드 시간을 줄이고 반복적인 개발을 더 단순하게 만들기 위해서입니다. 만약 이 애셋번들이 애플리케이션 자체로부터 개별적으로 갱신될 필요가 없다면, 애셋번들은, 스트리밍 애셋에 애셋번들을 저장함으로써, 애플리케이션에 포함될 수 있습니다. 아래의 Streaming Assets 섹션을 참고하십시오.
- 업데이트가 가능한 칸텐트의 초기 리비전을 포함시키기 위해서입니다. 이는 보통 초기 설치 이후에 엔드유저의 시간을 절약하기 위함이거나 나중의 패칭을 위한 기반의 역할을 하기 위해서입니다. Streaming Assets 는 이 경우에 이상적입니다. 그러나 커스텀 다운로딩 및 캐싱 시스템을 선택할 수 없다면, 업데이트가능한 칸텐트의 초기 리비전은 스트리밍 애셋에서 유니티 캐시로 로드될 수 있습니다.
4.2.1.1. Streaming Assets
설치시에 유니티 애플리케이션에 모든 유형의 칸텐트를 포함하는 가장 쉬운 방법은 프로젝트를 빌드하기 전에 칸텐트를 /Assets/StreamingAssets/ 폴더에 빌드하는 것입니다. 빌드시에 StreamingAssets 폴더 내에 포함된 모든 애셋은 최종 애플리케이션으로 복사됩니다. 이 폴더는 애셋번들 뿐만 아니라 최종 애플리케이션 내의 모든 유형의 칸텐트를 저장하기 위해 사용될 수 있습니다.
로컬 저장소 상에서 StreamingAssets 폴더의 전체 경로는 런타임에 Application.streamingAssetsPath 프라퍼티를 통해서 접근할 수 있습니다. 대부분의 플랫폼에서 애셋번들들은 AssetBundle.LoadFromFile 을 통해 로드될수 있습니다.
안드로이드 개발자: 안드로이드에서 Application.streamingAssetsPath 는 압축된 .jar 파일을 가리킬 것입니다. 마치 애셋번들이 압축된 것처럼 말이죠. 이 경우, WWW.LoadFromCacheOrDownload 을 호출해서 개별 애셋번들을 로드해야 합니다. 또한 .jar 파일을 압축해제하기 위한 커스텀 코드를 작성하고 애셋번들을 로컬 저장소의 읽기가능 위치로 추출하는 것도 가능합니다.
노트: 스트리밍 애셋은 특정 플랫폼에서는 쓰기가능한 위치가 아닙니다. 만약 프로젝트의 애셋번들이 설치 후에 갱신될 필요가 있다면, WWW.LoadFromCacheOrDownload 를 사용하거나 커스텀 다운로더를 작성하십시오. 세부사항을 원한다면 Custom downloaders - storage 섹션을 참고하십시오.
4.2.2. Downloaded post-install
애셋번들을 모바일 디바이스로 전송하기 위해 선호되는 기법은 번들을 앱 설치 후에 다운로드하는 것입니다. 이는 사용자가 전체 애플리케이션을 다시 다운로드하게 하지 않고도 설치 후에 새로운 혹은 변경된 칸텐트들을 사용해 칸텐트들이 업데이트될 수 있도록 해 줍니다. 모바일 플랫폼에서, 애플리케이션 바이너리들은 비싸고 복잡한 재인증 절차를 밟아야만 합니다. 그러므로 post-install 다운로드를 위한 좋은 시스템을 개발하는 것이 중요합니다.
애셋번들을 전송하기 위한 가장 단순한 방법은 웹 서버에 그것을 배치하고 WWW.LoadFromCacheOrDownload 혹은 UnityWebRequest 를 통해 전송하는 것입니다. 유니티는 자동으로 다운로드된 애셋번들을 로컬 저장소에 캐싱합니다. 만약 다운로드된 애셋번들이 LZMA 로 압축이 되어 있다면, 그 애셋번들은 추후의 빠른 로딩을 위해 압축이 안 된 캐시로 저장될 것입니다. 만약 다운로드된 번들이 LZ4 로 압축이 되어 있다면, 애셋번들은 압축된 상태로 저장될 것입니다.
만약 캐시가 꽉 차면, 유니티는 가장 최근에 사용되지 않은 애셋번들을 캐시에서 제거합니다. 세부사항을 원하면 Built-in caching 을 참고하십시오.
WWW.LoadFromCacheOrDownload 는 결점을 가지고 있음에 주의하십시오. Loading AssetBundles 섹션에서 기술했듯이, WWW 오브젝트는 다운로드를 하는 동안 애셋번들의 데이터와 같은 크기의 메모리를 소비합니다. 이는 원치않는 메모리 스파이크를 유발할 수 있습니다. 이를 피하기 위한 세 가지 방법이 있습니다:
- 애셋번들을 작은 크기로 만드십시오. 번들이 다운로드되고 있을 때 애셋번들의 최대 크기는 프로젝트의 메모리 예산에 의해 결정될 것입니다. 다운로딩 스크린을 가진 애플리케이션은 애셋번들을 백그라운드에서 스트리밍하는 애플리케이션보다 더 많은 메모리를 할당할 수 있습니다.
- 만약 유니티 5.3 이상 버전을 사용하고 있다면, 새로운 UnityWebRequest API 의 DownloadHandlerAssetBundle 로 전환하시기 바랍니다. 이는 다운로드 동안 메모리 스파이크를 일으키지 않습니다.
- 커스텀 다운로더를 작성하십시오. 더 많은 정보를 원한다면 Custom downloaders 섹션을 참고하십시오.
가능하면 UnityWebRequest 를 사용해서 시작하는 것을 권합니다. 만약 5.2 이전 버전을 사용한다면 WWW.LoadFromCacheOrDownload 를 사용하십시오. 만약 built-in API 의 메모리 소비, 캐싱, 동작, 성능이 특정 프로젝트에서 원치 않는 결과를 보여 준다면, 혹은 프로젝트가 플랫폼-특정 코드를 실행해서 그것의 요구를 만족시켜야만 한다면, 커스텀 다운로드 시스템에 투자하십시오.
UnityWebRequest 나 WWW.LoadFromCacheOrDownload 의 사용을 막아야 하는 경우는 다음과 같습니다:
- 애셋번들 캐시에 대한 fine-grained 제어가 요구될 때
- 프로젝트가 커스텀 압축 전략을 구현할 필요가 있을 때
- 프로젝트가 플랫폼-특정 API 를 사용해서, 비활성화되어 있는 동안 데이터를 스트림하고자 하는 요구같은, 특정 요구를 만족시키고자 할 때
- 예: iOS 백그라운드 태스크 API 를 사용해 백그라운드에서 데이터를 다운로드하고자 할 때
- ( PC 처럼 ) 적절한 SSL 지원을 가지고 있지 않은 플랫폼 상에서, SSL 을 통해 전송되어야만 할때
4.2.3. Built-in caching
유니티는 빌트인 애셋번들 캐싱 시스템을 가지고 있는데, 이는 WWW.LoadFromCacheOrDownload 나 UnityWebRequest API 를 통해 다운로드된 애셋번들을 캐싱하기 위해서 사용될 수 있습니다.
두 API 는 모두 애셋번들 버전 번호를 인자로 받는 오우버로드 메서드를 가지고 있습니다. 이 번호는 애셋번들 내에 저장되는 것이 아니며, 애셋번들 시스템에 의해서 생성되지도 않습니다.
캐싱 시스템은 WWW.LoadFromCacheOrDownload 나 UnityWebRequest 에 넘겨진 마지막 버전 번호의 기록을 유지합니다. 다른 API 가 버전 번호와 함께 호출될 때, 캐싱 시스템은 캐싱된 애셋번들이 존재하는지를 확인합니다. 만약 존재한다면, 그것은 애셋번들이 처음 캐싱되었을 때 넘겨진 버전 번호와 현재 호출에 넘겨진 버전 번호를 비교합니다. 만약 이 번호가 일치하면, 시스템은 캐싱된 애셋번들을 로드하게 됩니다. 만약 번호가 일치하지 않거나 캐싱된 애셋번들이 존재하지 않는다면, 유니티는 새로운 복사본을 다운로드합니다. 이 새로운 복사본은 새로운 버전 번호와 연관됩니다.
캐싱 시스템 내의 애셋번들은 그것들이 다운로드되었던 전체 URL 에 의해서가 아니라 파일 이름에 의해서만 식별됩니다. 이는 같은 이름을 가진 애셋번들이 서로 다른 위치에 저장될 수도 있다는 것을 의미합니다. 예를 들면 애셋번들은 CDN( Content Delivery Network )의 다중 서버 상에 배치될 수 있습니다. 파일 이름이 동일한 이상, 캐싱 시스템은 그것들을 같은 애셋번들로 인지합니다.
애셋번들에 버전 번호를 할당하고 이 번호를 WWW.LoadFromCacheOrDownload 에 넘기기 위한 적절한 전략을 세우는 것은 개별 애플리케이션에 달려 있습니다. 대부분의 애플리케이션들은 유니티 5 의 AssetBundleManifest API 를 사용할 수 있습니다. 이 API 는 애셋번들 칸텐트의 MD5 해시를 계산함으로써 각 애셋번들의 버전 번호를 생성합니다. 애셋번들이 변경될 때마다 그것의 해시는 변경되며, 이는 그 애셋번들이 다운로드되어야만 한다는 것을 지시합니다.
노트: 유니티 빌트인 캐시 구현의 특이점( quirk ) 때문에, 예전 애셋번들은 캐시가 꽉 차기 전까지는 삭제되지 않을 것입니다. 유니티는 앞으로의 릴리스에서 이 특이점에 대해 고심할 의도를 가지고 있습니다.
세부사항을 원한다면 Patching with AssetBundles 섹션을 참고하십시오.
유니티의 빌트인 캐싱은 Caching 오브젝트 상의 API 를 호출함으로써 제어될 수 있습니다. 유니티 캐시의 동작은 Caching.expirationDelay 와 Caching.maximumAvailableDiskSpace 를 변경함으로써 제어될 수 있습니다.
Caching.expirationDelay 는 애셋번들이 자동으로 제거되기 전에 기다려야 할 최소한의 시간( 초 )입니다. 만약 이 시간 동안 애셋번들에 대한 접근이 없으면, 그것은 자동으로 지워집니다.
Caching.maximumAvailableDiskSpace determines the amout of space on local storage that the cache may use before it begings deleting AssetBundles that have been used less recently than the expirationDelay. 그것은 바이트 단위로 카운팅됩니다. 제한에 도달하게 되면, 유니티는 가장 옛날에 열린( 혹은 Caching.MarkedAsUsed 로 마킹된 ) 캐시에서 애셋번들을 제거합니다. 유니티는 새로운 다운로드를 완료하기 위해 충분한 공간을 확보할 때까지 캐싱된 애셋번들을 제거할 것입니다.
노트: 유니티 5.3 에서는 빌트인 캐시에 대한 제어가 매우 거칩니다. 특정 애셋번들을 캐시에서 제거하는 것은 불가능합니다. 그것들은 expiration 이나 디스크 공간 부족, 혹은 Caching.CleanCache 호출을 통해서만 제거될 수 있습니다( Caching.CleanCache 는 현재 캐시에 있는 모든 애셋번들을 제거하게 됩니다 ). 이는 개발이나 라이브 연산 동안에는 문제가 될 수 있습니다. 왜냐하면 유니티가 애플리케이션에 의해서 더 이상 사용되지 않는 애셋번들을 자동으로 지워주지 않기 때문입니다.
4.2.3.1. Cache Priming
애셋번들은 파일 이름으로 식별되기 때문에, 애플리케이션에 포함된 애셋번들을 사용하는 캐시를 "미리 지정하는( prime )" 것이 불가능합니다. 이를 위해서는, 각 애셋번들의 초기 혹은 기본 버전을 /Assets/StreamingAssets/ 에 저장하십시오. 그 절차는 Shipped with project 섹션에서 세부적으로 설명한 것과 동일합니다.
애플리케이션이 처음 실행될 때 Application.streamingAssetsPath 로부터 애셋번들을 로딩함으로써 캐시가 생성될 수 있습니다. 그 때부터, 애플리케이션은 WWW.LoadFromCacheOrDownloads 나 UnityWebRequest 를 정상적으로 호출할 수 있습니다.
4.2.4. Custom downloaders
커스텀 다운로더를 작성하는 것은 애플리케이션으로 하여금 애셋번들을 다운로드하고, 압축해제하고, 저장하는 것과 관련한 모든 제어를 할 수 있게 해 줍니다. 커스텀 다운로더를 작성하는 것은 야심찬 애플리케이션을 작성하고 있는 큰 팀들에 대해서만 권장됩니다. 커스텀 다운로더를 작성하는 동안 생각해야 하는 네 가지 정도의 문제가 있습니다:
- 애셋번들을 다운로드하는 방법.
- 애셋번들을 저장할 위치.
- 애셋번들을 압축하는 방법이나 압축할지 여부.
- 애셋번들을 패치하는 방법.
애셋번들을 패치하는 것과 관련한 더 많은 정보를 원한다면, Patching with AssetBundles 섹션을 참고하십시오.
4.2.4.1. Downloading
대부분의 애플리케이션에서, HTTP 는 애셋번들을 다운로드하는 가장 단순한 방법입니다. 그러나 HTTP 기반 다운로더를 구현하는 것은 단순한 작업이 아닙니다. 커스텀 다운로더는 너무 많은 메모리를 할당하는 행위, 너무 많은 스레드를 사용하는 행위 등을 피해야만 합니다. 유니티의 WWW 클래스는 여기에서 속속들이 설명한 이유 때문에 부적절합니다. WWW 는 높은 메모리 비용을 가지고 있으므로, WWW.LoadFromCacheOrDownload 클래스를 사용하는 경우가 아니라면 유니티의 WWW 클래스를 사용하는 것은 피하는 것이 좋습니다.
커스텀 다운로더를 작성할 때, 세 가지 옵션이 있습니다:
- C# 의 HttpWebRequest 와 WebClient 클래스.
- 커스텀 네이티브 플러그인.
- 애셋 스토어 패키지.
4.2.4.1.1. C# classes
애플리케이션이 HTTPS/SSL 지원을 요구하지 않는다면, C# 의 WebClient 클래스는 애셋번들을 다운로드하기 위한 가장 단순한 가능성있는 메커니즘을 제공합니다. 그것은 관리되는 메모리 할당을 초과하는 일이 없이 로컬 저장소에 파일을 바로 다운로드하는 기능을 제공합니다.
애셋번들을 WebClient 를 통해 다운로드하기 위해서는, 클래스의 인스턴스를 할당하고 그것에 다운로드할 애셋번들의 URL 과 대상 경로를 넘깁니다. 만약 요청 파라미터에 대한 더 많은 제어를 원한다면, HttpWebRequest 클래스를 사용해서 다운로더를 작성하는 것이 가능합니다.
- HttpWebResponse.GetResponseStream 으로부터 바이트 스트림을 획득합니다.
- 고정 크기 바이트 버퍼를 스택에 할당합니다.
- 응답 스트림으로부터 읽어들여 버퍼에 씁니다.
- C# File.IO API 를 사용하거나 다른 스트리밍 IO 시스템을 사용해서 버퍼를 디스크에 씁니다.
플랫폼 노트: iOS, 안드로이드, 윈도우즈 폰은 유니티 C# 런타임이 C# HTTP 클래스들을 위한 HTTPS/SSL 지원을 포함하고 있는 유일한 플랫폼들입니다. PC 에서는, C# 클래스를 통해 HTTPS 서버에 접근하는 시도가 인증서 검증 에러( certificate validation error )를 발생시킬 것입니다.
4.2.4.1.2. Asset Store Packages
몇 개의 애셋 스토어 패키지들은 HTTP, HTTPS, 그리고 다른 프로토콜들을 통해서 파일을 다운로드하기 위한 네이티브 코드 구현을 제공하고 있습니다. 커스텀 네이티브 코드 플러그인을 작성하기 전에, 이용가능한 애셋 스토어 패키지들을 평가해 보실 것을 권합니다.
4.2.4.1.3. Custom Native Plugins
커스텀 네이티브 플러그인을 작성하는 것은 매우 시간이 많이 걸리는 작업이며 유니티에서 데이터를 다운로딩하기 위한 더욱 유연한 기법입니다. 프로그래밍 시간이 오래 걸리고 기술적 위험도가 높으므로, 이 기법은 다른 기법들이 애플리케이션의 요구사항을 만족시켜주지 못했을 때만 사용할 것을 권합니다. 예를 들어, 커스텀 네이티브 플러그인은 유니티의 C# SSL 지원이 없는 플랫폼( 윈도우즈, OSX, Linux )에서 애플리케이션이 SSL 통신을 해야만 하는 경우가 있습니다.
커스텀 네이티브 플러그인은 일반적으로 대상 플랫폼의 네이티브 다운로딩 API 에 대한 래퍼일 것입니다. iOS 의 NSURLConnection 과 안드로이드의 java.net.HttpURLConnection 이라는 예제가 있습니다. 이러한 API 를 사용하는 데 있어서의 세부사항을 원한다면 각 플랫폼의 네이티브 문서를 참고하십시오.
4.2.4.2. Storage
모든 플랫폼에서, Application.persistentDataPath 는 애플리케이션을 여러 번 실행하는 동안 지속적으로 유지되어야 하는 데이터를 저장하기 위해서 사용되어야 하는 쓰기가능한 위치를 가리킵니다. 커스텀 다운로더를 사용할 때는, Application.persistentDataPath 의 하위 디렉토리에 다운로드된 데이터를 저장할 것을 강력히 권합니다.
Application.streamingAssetPath 는 쓰기가능한 위치가 아니며, 애셋번들 캐시를 위해서는 좋지 않은 선택입니다. StreamingAssetsPath 가 포함하는 위치의 예는 다음과 같습니다:
- OSX: .app 패키지 내; 쓰기 불가능.
- Windows: 설치 디렉토리 내( 예를 들어 program files ); 보통 쓰기 불가능.
- iOS: ipa 패키지 내; 쓰기 불가능.
- Android: 압축된 .jar 파일 내; 쓰기 불가능.
4.3. Asset Assignment Strategies
프로젝트의 애셋을 애셋번들로 어떻게 나눌지를 결정하는 것은 쉽지 않은 문제입니다. 모든 오브젝트에 대해 각각의 애셋번들을 만들거나 하나의 애셋번들만 사용하는 것과 같은 단순한 전략을 취하고 싶은 충동을 느끼게 될 것입니다. 하지만 이러한 해결책은 심각한 단점들을 가지고 있습니다:
- 너무 적은 애셋번들을 가지고 있습니다...
- 런타임 메모리 사용량이 증가합니다.
- 로딩 시간이 증가합니다.
- Requires larger downloads
- 너무 많은 애셋번들을 가지고 있습니다...
- 빌드 시간이 증가합니다.
- 개발이 복잡해집니다.
- 전체 다운로드 시간이 증가합니다.
핵심 결정 사항은 오브젝트를 애셋번들에 어떻게 그룹화하느냐입니다. 주요 전략은 다음과 같습니다:
- 논리적 요소들.
- 오브젝트 유형들.
- 동시성 칸텐트.
단일 프로젝트는 서로 다른 칸텐트 카테고리들을 위해 이들 전략을 섞을 수 있고 섞어야만 합니다. 예를 들어, 프로젝트는 UI 요소들을 서로 다른 플랫폼을 위한 애셋번들로 그룹화할 수 있습니다. 그러나 그것의 인터랙티브 칸텐트는 레벨이나 씬 단위로 그룹화합니다. 적용한 전략과 관계없이, 다음은 좋은 가이드라인이 됩니다:
- 자주 갱신되는 오브젝트는 보통 변경되지 않는 오브젝트와는 다른 애셋번들로 나눕니다.
- 동시에 로드될 것 같은 오븢게트는 함께 그룹화합니다.
예: 모델, 그것의 애니메이션, 텍스쳐.
- 만약 오브젝트가 서로 다른 애셋번들 내에 있는 다수 개의 오브젝트들에 대한 종속성을 가지고 있다면, 그 애셋을 개별 애셋번들로 이동시키십시오.
- 이상적으로 볼 때, 자식 오브젝트들을 그것의 부모 오브젝트들과 함께 그룹화하는 것이 좋습니다.
- 만약 ( 텍스쳐의 HD 및 SD 버전 처럼 ) 두 개의 오브젝트들이 동시에 로드되는 것을 원하지 않는다면, 그것들을 개별 애셋번들로 나누십시오.
- 만약 오브젝트들이 서로 다른 임포터 세팅이나 데이터를 가지고 있기 때문에 같은 오브젝트에 대한 서로 다른 버전이 된 것이라면, 애셋번들 Variants 를 대신 사용하십시오.
Once the above guideline are followed, 애셋번들 칸텐트의 50% 보다 적은 부분이 주어진 시간에 로드된다면 애셋번들을 분리하는 것을 고려하시기 바랍니다. 또한 동시에 로드되는 작은 애셋번들( 5 ~ 10 개 보다 적은 애셋 )들을 합치는 것을 고려하시기 바랍니다.
4.3.1. Logical entity grouping
논리적 엔터니 그룹화 전략에서는 오브젝트들이 그것들이 제공하는 프로젝트의 기능적 부분에 기반해서 그룹화됩니다. 이 전략에 따르면, 애플리케이션의 서로 다른 부분들은 서로 다른 애셋번들로 분리됩니다.
예:
- UI 스크린을 위한 모든 텍스쳐와 레이아웃 데이터를 함께 번들로 묶습니다.
- 캐릭터 셋을 위한 텍스쳐, 모델, 애니메이션을 함께 번들로 묶습니다.
- 많은 레벨에서 공유되는 배경 조각들을 위한 텍스쳐와 모델들을 함께 번들로 묶습니다.
논리적 엔터티 그룹화는 가장 일반적인 애셋번들 전략입니다. 그리고 이는 다음과 같은 상황에 특히 적합합니다:
- DLC( 역주 : Downloadable Content ).
- 애플리케이션 생명주기 전반에 걸쳐 많은 곳에서 나타나는 엔터티들.
예:
- 공통 캐릭터나 기본 UI 요소들.
- 플랫폼이나 성능 세팅에 기반해서 다양해지는 엔터티들.
애셋을 논리적 엔터티에 의해서 그룹화하는 것의 장점은 변경되지 않은 칸텐트들을 다시 다운로드하지 않고도 개별 엔터티들을 쉽게 갱신할 수 있게 한다는 것입니다. 이것이 이 전략이 DLC 를 위해 특히 적합한 이유입니다. 이 전략은 대부분 메모리에 대해 효율적입니다. 왜냐하면 애플리케이션은 현재 사용중인 엔터티를 제출하는 애셋번들만을 로드할 필요가 있기 때문입니다.
그러나 이 전략은 구현하기 애매한 전략입니다. 왜냐하면 개발자들이 개별 오브젝트가 프로젝트에 의해서 언제 어떻게 사용되는지를 정확하게 알아야만 오브젝트들을 애셋번들에 할당할 수 있기 때문입니다.
4.3.2. Type Grouping
타입 그룹화는 가장 단순한 전략입니다. 이 전략에서, 비슷하거나 동일한 타입을 가진 오브젝트들이 같은 애셋번들에 묶입니다. 예를 들어, 여러 개의 서로 다른 오디오 트랙들이 애셋번들에 배치되거나, 여러 개의 서로 다른 언어 파일들이 애셋번들에 배치될 수 있습니다.
이 전략은 단순하지만, 빌드 시간, 로딩 시간, 업데이트 시간의 관점에서 봤을 때는 가장 비효율적입니다. 이 전략은 지역화 파일들처럼 작고 동시에 갱신되는 파일들을 위해서 자주 사용됩니다.
4.3.3. Concurrent content grouping
동시 칸텐트 그룹화는 오브젝트들이 동시에 로드되고 사용될 때 하나의 애셋번들로 묶는 전략입니다. 이 전략은 칸텐트가 매우 지역적인 프로젝트에서 보통 사용됩니다: 여기에서 칸텐트는 애플리케이션의 특정 위치나 시점에서 벗어나서 나타나지 않습니다. 예를 들어, 개별 레벨마다 유일한 아트, 캐릭터, 사운드 이펙트가 나오는 레벨 기반 게임을 들 수 있습니다.
동시-칸텐트 그룹화를 수행하기 위한 가장 일반적인 기법은 씬에 기반해 애셋번들을 생성하는 것입니다. 이 때 씬의 종속성을 거의 혹은 모두 포함하는 씬 기반 애셋번들을 사용합니다.
칸텐트가 매우 지역적이지 않거나 칸텐트가 애플리케이션 생명주기 동안에 다양한 위치에서 나타나는 프로젝트의 경우에는, 동시 칸텐트 그룹화는 논리적 엔터티 그룹화와 결합됩니다. 둘다 주어진 애셋번들의 칸텐트의 유용성을 최대화하기 위해서 필수적인 전략들입니다.
이 시나리오의 예는 오픈월드 게임입니다. 여기에서는 캐릭터들이 랜덤하게 스폰되며 월드 공간에 펴져 있습니다. 이 경우, 캐릭터가 동시에 어디에서 나타날지 예측하는 것은 쉽지 않습니다. 그래서 그것들은 일반적으로 서로 다른 전략을 사용해서 그룹화되어야 합니다.
4.4. Patching with AssetBundles
애셋번들을 패치하는 것은 새로운 애셋번들을 다운로드하고 그것을 현존하는 것과 교체하면 되기 때문에 매우 단순합니다. 만약 애플리케이션의 캐싱된 애셋번들을 관리하기 위해서 WWW.LoadCacheOrDownload 나 UnityWebRequest 가 사용되었다면, 이는 선택된 API 에 다른 version 매개변수를 넘기기만하므로 단순합니다( 더 많은 세부사항을 원한다면 스크립팅 레퍼런에 대한 위의 링크를 참고하세요 ).
패칭 시스템에 있어서 해결하기 어려운 문제는 어떠한 애셋번들이 대체되어야 하는지를 찾는 것입니다. 패칭 시스템은 두 개의 리스트를 요구합니다:
- 현재 다운로드된 애셋번들과 그것들의 버전 정보 리스트.
- 서버 상의 애셋번들과 그것들의 버전 정보 리스트.
패쳐는 서버측의 애셋번들 리스트를 다운로드하여 애셋번들 리스트와 비교해야 합니다. Missing 애셋번들이나 버전 정보가 변한 애셋번들은 다시 다운로드되어야 합니다.
유니티 5 의 애셋번들 시스템은 빌드가 완료되었을 때 새로운 부가적인 애셋번들을 생성합니다. 이 부가적인 애셋번들은 AssetBundleManifest 오브젝트를 포함합니다. 이 매니페스트 오브젝트는 애셋번들의 리스트와 그것의 해시를 포함하고 있으며, 그것은 이용가능한 애셋번들의 리스트와 버전 정보를 클라이언트에 전송하기 위해서 사용됩니다. 애셋번들 매니페스트 번들에 대한 더 많은 정보를 원한다면 유니티 매뉴얼을 참고하세요.
애셋번들의 변화를 검색하는 커스텀 시스템을 작성하는 것도 가능합니다. 자신만의 시스템을 작성하는 대부분의 개발자들은 애셋번들 파일 리스트를 위해 JSON 같은 업계-표준 데이터 포맷을 사용하며, MD5 와 같은 체크섬을 계산하기 위해서 표준 C# 클래스를 사용합니다.
4.4.1. Differential patching
유니티 5 에서, 유니티는 결정론적인 방식으로 순서화된 데이터를 사용해 애셋번들을 빌드할 수 있습니다. 이는 커스텀 다운로더를 가진 애플리케이션이 differential patching( 역주 : 미분 패칭? ) 을 구현할 수 있도록 해 줍니다. 결정론적 레이아웃을 사용해서 애셋번들을 빌드하기 위해서는, BuildAssetBundleOptions.DeterministicAssetBundle 플래그를 BuildAssetBundles API 를 호출할 때 넘깁니다( 더 많은 세부 사항을 원한다면 스크립팅 레퍼런스 링크를 참고하세요 ).
유니티는 differential patching 을 위한 내장 메커니즘을 제공하지 않습니다. 그리고 WWW.LoadFromCachedOrDownload 나 UnityWebRequest 는 내장 캐싱 시스템을 사용할 때 differential patching 을 수행하지 않습니다. 만약 differntial pathcing 이 요구되면, 커스텀 다운로더를 작성해야 합니다.
4.4.2. iOS On-Demand Resources
온디맨드 리소스는 iOS 와 TVOS 장치에 칸텐트를 제공하기 위한 애플 API 입니다. 이는 iOS 9 장치에서 이용할 수 있습니다. 이것은 앱스토어 상에 런칭하는 데는 요구되지 않습니다만, TVOS 앱을 위해서는 요구됩니다.
애플의 온디맨드 리소스 시스템의 일반적인 개요는 애플 개발자 사이트에서 찾아볼 수 있습니다.
As of Unity 5.2.1, support for App Slicing and On-Demand Resources are both built upon another Apple system, Asset Catalogs. After registering a callback in the Unity Editor, the build pipeline for an iOS applications can report a set of files which will be automatically placed into Asset Catalogs and assigned specified On-Demand Resources tags.
A new UnityEngine.iOS.OnDemandResources API provides runtime support for retrieving and caching On-Demand Resources files. Once resources have been retrieved via ODR, they can then be loaded into Unity via the standard AssetBundle.LoadFromFile API.
For more details and an example project, see this Unity forum post.
4.5. Common Pitfalls
이 섹션은 애셋번들을 사용할 때 프로젝트에서 일반적으로 나타날 수 있는 몇 가지 문제들에 대해서 기술합니다.
4.5.1. Asset duplication
유니티 5 의 애셋번들 시스템은 오브젝트가 애셋번들에서 빌드될 때 오브젝트의 모든 종속성을 고려하지 않습니다. 이는 애셋 데이터베이스를 사용해서 수행됩니다. 이 종속성 정보는 애셋번들에 포함될 오브젝트 집합을 결정하는데 사용하는 정보입니다.
애셋번들에 명시적으로 할당된 오브젝트들은 그 애셋번들로 빌드되어야 할것입니다. 오브젝트의 AssetImporter 가 그것의 assetBundleName 속성을 비어있지 않은 문자열로 설정할 때 오브젝트는 "명시적으로" 할당됩니다. 이는 유니티 에디터에서 수행되는데, 오브젝트의 인스펙터나 에디터 스크립트로부터 애셋번들을 선택함으로써 수행될 수 있습니다.
애셋번들에 명시적으로 할당되지 않은 모든 오브젝트는 표시되지 않은 오브젝트를 참조하는 하나 이상의 오브젝트를 포함하는 모든 애셋번들들에 포함될 것입니다.
만약 두 개의 오브젝트가 서로 다른 애셋번들에 할당되었지만 둘다 공통 종속성 오브젝트를 참조하고 있다면, 그 종속성 오브젝트는 두 애셋번들에 모두 복사됩니다. 중복된 종속성이 인스턴스화될 것입니다. 이는 종속성 오브젝트에 대한 두 개의 복사본이 서로 다른 식별자를 가진 서로 다른 오브젝트로 고려된다는 것을 의미합니다. 이는 애플리케이션의 애셋번들의 전체 크기를 증가시킵니다. 또한 애플리케이션이 그것의 부모를 모두 로드한다면, 오브젝트의 서로 다른 복사본이 메모리에 로드될 것입니다.
이 문제를 해결하기 위한 두 가지 방법이 있습니다:
- 서로 다른 애셋번들에 빌드된 오브젝트가 같은 종속성을 가지지 않도록 합니다. 종속성을 공유하는 모든 오브젝트들은 자신의 종속성들을 복사하지 않고 같은 애셋번들에 배치될 수 있습니다.
- 이 기법은 많은 공유 종속성을 가진 프로젝트에 대해서는 항상 실행가능한 것이 아닙니다. 이는 모놀리식 애셋번들을 생성하며, 그 애셋번들은 편의성과 효율성을 위해서 너무 자주 리빌드되거나 다시 다운로드되어야만 합니다.
- 애셋번들을 세그먼트화하면 종속성을 공유하는 두 개의 애셋번들이 동시에 로드되지 않을 것입니다.
- 이 기법은 레벨 기반 게임과 같은 특정 유형의 프로젝트에 대해서 작동할 것입니다. 그러나 여전히 프로젝트의 애셋번들의 크기, 빌드 시간, 로딩 시간이 불필요하게 늘어납니다.
- 종속성 애셋이 자신만의 애셋번들에 빌드되도록 합니다. 이는 전체적으로 중복 애셋의 위험성을 제거합니다만, 복잡도를 증가시킵니다. 애플리케이션은 애셋번들 간의 종속성을 추적해서 AssetBundle.LoadAsset API 호출 전에 올바른 애셋번들이 로드되어 있도록 해야만 합니다.
유니티 5 에서, 오브젝트 종속성은 AssetDatabase API 를 통해 추적될 수 있습니다. 이는 UnityEditor 네임스페이스에 존재합니다. 네임스페이스가 내포하고 있듯이, 이 API 는 유니티 에디터에서만 사용할 수 있으며 런타임에는 사용할 수 없습니다. AssetDatabase.GetDependencies 는 특정 오브젝트나 애셋의 즉각적인 종속성을 모두 찾아내기 위해서 사용됩니다. 이 종속성들은 자신만의 종속성들을 가질 수도 있다는 점에 주의하십시오. 부가적으로, AssetImporter API 는 특정 오브젝트가 할당된 애셋번들을 질의하는데 사용될 수 있습니다.
AssestDatabase 와 AssetImporter API 를 혼용함으로써, 애셋번들의 직접적인 혹은 간접적인 종속성들을 모두 애셋번들에 할당하거나 애셋번들에 할당되지 않은 종속성을 애셋번들이 공유하고 있는지를 확인하는 에디터 스크립트를 작성하는 것이 가능합니다. 애셋이 중복되는 것의 메모리 비용 때문에, 모든 프로젝트에서 그런 스크립트를 제작하기를 권장합니다.
4.5.2. Sprite atlas duplication
다음 섹션은 자동으로 생성된 스프라이트 아틀라스를 결합했을 때 유니티 5 애셋 종속성 계산 코드의 특이함에 대해 기술합니다. 유니티 5.2.2p4 와 유니티 5.3 은 이 동작을 해결하기 위해서 패치되었습니다.
Unity 5.2.2p4, 5.3 and newer
자동으로 생성된 모든 스프라이트 아틀라스들은 스프라이트 아틀라스가 생성된 스프라이트 오브젝트를 포함하는 애셋번들에 할당될 것입니다. 만약 스프라이트 오브젝트가 다수개의 애셋번들에 할당되었다면, 스프라이트 아틀라스는 애셋번들에 할당되지 않고 중복되게 될 것입니다. 또한 만약 스프라이트 오브젝트가 애셋번들에 할당되지 않았다면, 스프라이트 아틀라스는 애셋번들에 할당되지 않을 것입니다.
스프라이트 아틀라스가 중복되지 않도록 하기 위해서는 같은 스프라이트 아틀라스라고 표기된 모든 스프라이트들이 같은 애셋번들에 할당되었는지 확인하십시오.
Unity 5.2.2p3 and older
Automatically-generated sprite atlases will never be assigned to an AssetBundle. Because of this, they will be included in any AssetBundles containing their constituent sprites and also any AssetBundles referencing their constituent sprites.
Because of this problem, it is strongly recommended that all Unity 5 projects using Unity's sprite packer upgrade to Unity 5.2.2p4, 5.3 or any newer version of Unity.
For projects that cannot upgrade, there are two workarounds for this problem:
- Easy: Avoid using Unity's built-in sprite packer. Sprite atlases generated by external tools will be normal Assets, and can be properly assigned to an AssetBundle.
- Hard: Assign all Objects that use automatically atlased sprites to the same AssetBundle as the sprites.
- This will ensure that the generated sprite atlas is not seen as the indirect dependency of any other AssetBundles and will not be duplicated.
- This solution preserves the simple workflow of using Unity's sprite packer, but it degrades developers' ability to separate Assets into different AssetBundles, and forces the re-download of an entire sprite atlas when any data changes on any component referencing the atlas, even if the atlas itself is unchanged.
4.5.3. Android textures
Due to heavy device fragmentation in the Android ecosystem, it is often necessary to compress textures into several different formats. While all Android devices support ETC1, ETC1 does not support textures with alpha channels. Should an application not require OpenGL ES 2 support, the cleanest way to solve the problem is to use ETC2, which is supported by all Android OpenGL ES 3 devices.
Most applications need to ship on older devices where ETC2 support is unavailable. One way to solve this problem is with Unity 5's AssetBundle Variants. (Please see Unity's Android optimization guide for details on other options.)
To use AssetBundle Variants, all textures that cannot be cleanly compressed using ETC1 must be isolated into texture-only AssetBundles. Next, create sufficient variants of these AssetBundles to support the non-ETC2-capable slices of the Android ecosystem, using vendor-specific texture compression formats such as DXT5, PVRTC and ATITC. For each AssetBundle Variant, change the included textures' TextureImporter settings to the compression format appropriate to the Variant.
At runtime, support for the different texture compression formats can be detected using the SystemInfo.SupportsTextureFormat API. This information should be used to select and load the AssetBundle Variant containing textures compressed in a supported format.
More information on Android texture compression formats can be found here.
4.5.4. iOS file handle overuse
이 이슈는 5.3.2p2 에서 해결되었습니다. 현재 버전의 유니티는 이 이슈의 영향을 받지 않습니다.
In versions prior to Unity 5.3.2p2, Unity would hold an open file handle to an AssetBundle the entire time that the AssetBundle is loaded. This is not a problem on most platforms. However, iOS limits the number of file handles a process may simultaneously have open to 255. If loading an AssetBundle causes this limit to be exceeded, the loading call will fail with a "Too Many Open File Handles" error.
This was a common problem for projects trying to divide their content across many hundreds or thousands of AssetBundles.
For projects unable to upgrade to a patched version of Unity, temporary solutions are:
- Reducing the number of AssetBundles in use by merging related AssetBundles
- Using AssetBundle.Unload(false) to close an AssetBundle's file handle, and managing the loaded Objects' lifecycles manually
4.6. AssetBundle Variant
A key feature of Unity 5's AssetBundle system is the introduction of AssetBundle Variants. The purpose of Variants is to allow an application to adjust its content to better suit its runtime environment. Variants permit different UnityEngine.Objects in different AssetBundle files to appear as being the "same" Object when loading Objects and resolving Instance ID references. Conceptually, it permits two UnityEngine.Objects to appear to share the same File GUID & Local ID, and identifies the actual UnityEngine.Object to load by a string Variant ID.
There are two primary use cases for this system:
- Variants simplify the loading of AssetBundles appropriate for a given platform.
- Example: A build system might create an AssetBundle containing high-resolution textures and complex shaders suitable for a standalone DirectX11 Windows build, and a second AssetBundle with lower-fidelity content intended for Android. At runtime, the project's resource loading code can then load the appropriate AssetBundle Variant for its platform, and the Object names passed into the AssetBundle.Load API do not need to change.
- Variants allow an application to load different content on the same platform, but with different hardware.
- This is key for supporting a wide range of mobile devices. An iPhone 4 is incapable of displaying the same fidelity of content as an iPhone 6 in any real-world application.
- On Android, AssetBundle Variants can be used to tackle the immense fragmentation of screen aspect ratios and DPIs between devices.
4.6.1. Limitations
A key limitation of the AssetBundle Variant system is that it requires Variants to be built from distinct Assets. This limitation applies even if the only variations between those Assets is their import settings. If the only distinction between a texture built into Variant A and Variant B is the specific texture compression algorithm selected in the Unity texture importer, Variant A and Variant B must still be entirely different Assets. This means that Variant A and Variant B must be separate files on disk.
This limitation complicates the management of large projects as multiple copies of a specific Asset must be kept in source control. All copies of an Asset must be updated when developers wish to change the content of the Asset.
There are no built-in workarounds for this problem.
Most teams implement their own form of AssetBundle Variants. This is done by building AssetBundles with well-defined suffixes appended to their filenames, in order to identify the specific variant a given AssetBundle represents. Custom code programmatically alters the importer settings of the included Assets when building these AssetBundles. Some developers have extended their custom systems to also be able to alter parameters on components attached to prefabs.
4.7. Compressed or Uncompressed?
애셋번들을 압축할 것이냐 말 것이냐는 주의깊게 생각해 볼 주제입니다. 중요한 질문들은 다음과 같습니다:
- 애셋번들의 로딩 타임이 중요한 요소입니까? 압축 안 된 애셋번들이 압축된 애셋번들보다 로컬 저장소나 로컬 캐시에서 로드하는데 훨씬 빠릅니다. 압축된 애셋번들을 원격 서버에서 다운로드할 대는 압축 안 된 애셋번들을 다운로드할 때보다 빠릅니다.
- 애셋번들의 빌드 타임이 중요한 요소입니까? LZMA 와 LZ4 는 파일을 압축할 때 매우 느립니다. 그리고 유니티 에디터는 애셋번들을 직렬적으로 처리합니다. 많은 개수의 애셋번들을 포함하는 프로젝트는 그것들을 압축하는데 많은 시간을 소비하게 될 것입니다.
- 애플리케이션 크기가 중요한 요소입니까? 만약 애셋번들이 애플리케이션에 포함된다면, 그것들을 압축하는 것이 전체 애플리케이션의 크기를 줄이는데 도움이 될 것입니다. 대안적으로, 애셋번들을 post-install 에 다운로드할 수도 있습니다.
- 메모리 사용량이 중요한 요소입니까? 5.3 전에는 유니티의 압축해제 메커니즘이 압축해제를 하기 전에 전체 압축된 애셋번들을 메모리에 로드할 것을 요구했습니다. 만약 메모리 사용량이 중요하다면, 압축하지 말든가 LZ4 로 압축된 애셋번들을 사용하십시오.
- 다운로드 시간이 중요한 요소입니까? 애셋번들이 크거나 사용자가 모바일 3G 나 low-speed meterred connections 와 같은 대역폭이 제한된 환경에 있다면 압축이 필요할 것입니다. 만약 몇 십 메가바이트의 데이터를 high-speed connections 를 사용하는 PC 로 전송하고 있다면, 압축을 배제해도 될 것입니다.
4.8. AssetBundles and WebGL
유니티는 WebGL 프로젝트에서는 개발자들이 압축된 애셋번들을 사용하지 말도록 강력히 권하고 있습니다.
As of Unity 5.3, all AssetBundle decompression and loading in a WebGL project must occur on the main thread. This is because Unity 5.3's WebGL export option does not currently support worker threads. (The downloading of AssetBundles is delegated to the browser via the XMLHttpRequest Javascript API, and will occur off of Unity's main thread.) This means that compressed AssetBundles are extremely expensive to load on WebGL.
With this in mind, you may want to avoid using the default LZMA Format for your AssetBundles and compress using LZ4 instead, which is decompressed very efficiently on-demand. If you need smaller compression sizes then LZ4 delivers, you can configure your web server to gzip-compress the files on the http protocol level (on top of LZ4 compression).