행복한 연어의 이야기

(Unity) 유니티 에셋번들 본문

IT/Unity

(Unity) 유니티 에셋번들

해피살몬 2020. 2. 6. 19:11

에셋이란 ?

코드에서 변경하는 자원들 (보통 리소스 폴더에 있는 것들 - 프리팹, 사운드, 텍스쳐 등등 )

에셋번들이란 ?

위에서 설명한 에셋들을 가공(?)해서 묶어놓은 것들

에셋번들을 사용한다란 ?

리소스 폴더가 아닌 따로 지정한 경로(서버도 될 수 있음)에 있는 에셋들을 가져와서 사용하는 방법 (보통 모바일게임에서 게임 다운로드 후 접속하면 추가 다운로드를 하는 경우가 있는데 서버에서 에셋들을 추가 다운로드를 하는 것)

에셋번들을 사용하는 이유 ?

여러가지 이유가 있는데 기본적으로는 앱의 용량을 줄이기 위해서고 추가적으로는 업데이트가 용이 하기 떄문(서버에 에셋번들을 올려 받게 해두면 업데이트시 어플을 재설치할 필요없이 서버에 있는 에셋만 바꾸면 된다.)

1. 에셋번들의 2가지 구성요소

  1. 해더
    • 에셋번들의 식별자. 에셋번들이 압축된 상태인지 아닌지, 매니페스터 같은 에셋번들 정보 (매니페스트는 오브젝트의 이름이 키로 저장되어있는 검색 테이블)
  2. 세그먼트
    • 테이블의 각 엔트리(entry)는 에셋번들의 데이터 세그먼트에서 해당 오브젝트의 위치를 찾을 수 있는 인덱스를 제공한다.
      (대부분 균형 검색 트리 사용(balanced search tree), Windows 와 OSX계열 (IOS포함)플랫폼은 레드 블랙트리 사용)

*) 압축포맷
LZMA - 모든 에셋이 직렬화 한 다음, 전체 바이트 배열 압축, 전체 에셋번들 압축해제 필요
LZ4 - 에셋번들 내의 에셋을 개별적으로 압축, 개별오브젝트의 압축 해제 가능, 유니티 권장 압축방식

2. 에셋번들 압축하기

  1. BuildAssetBundleOptions.None
    • LZMA 압축포맷 사용
    • 파일 크기는 가장 작다. 전체를 압축 해제 하기때문에 압축 해제에 시간이 조금 더 걸린다.
  2. BuildAssetBundleOptions.UncompressedAssetBundle
    • 데이터가 전혀 압축되지 않음
    • 파일 크기가 가장 크다. 비교적 압축해제 속도가 빠르다.
  3. BuildAssetBundleOptions.ChunkBasedCompression
    • LZ4 압축 포맷 사용
    • 파일 크기 보통, 압축 해제속도 보통이다. 부분적으로 압축을 해제 한다.

3. 에셋번들 로딩 하기

  1. AssetBundle.LoadFromMemoryAsync
    • 사용 권장 X
    • 이렇게 불러온 에셋은 메모리에 총 3번 복사돠가 때문에 사용하지 말자 - 아주 오래된 방식
  2. AssetBundle.LoadFromFile - 로컬영역에 있는 에셋을 불러올때
    • 사용 권장 O
    • 하드디스크나 로컬 저장소에서 비압축 에셋번들을 로딩하기 위해 설계됨
    • LZMA 압축방식의 압축해제를 지원하기 않음
    • LZMA 압축방식 일시 번들을 메모리에 로드하기 전에 압축이 해제됨
    • 5.3 이하 버전에서는 사용 불가 5.4 이상 버전만 사용가능
  3. WWW.LoadFromCacheOrDownload - 서버에 있는 에셋을 불러올때
    • 5.2이하 버전에서 사용 O
    • 원격 서버 및 로컬 저장소에서 오브젝트를 로드 할때 유용
    • 에셋번들의 크기가 크면 메모리 오버해드가 일어남 (최대 몇 메가 바이트)
    • 호출할 때 쓰레드가 생성되기 때문에 동시에 호출 조심
  4. UnityWebRequest (AssetBundleDownloadHandler) - 서버에 있는 에셋을 불러올때
    • 5.3이상 버전에서 사용 O
    • 원격 서버 및 로컬 저장소에서 오브젝트를 로드 할때 유용
    • WWW.LoadFromCacheOrDownload 와 비슷한 방식으로 캐싱 지원
    • 호출할 때 쓰레드가 무한히 생기는 것을 막기 위해서 최대 크기가 고정된 쓰레드 풀을 가지고있음

*) 권장 사항
1-1. 일반적으로 AssetBundle.LoadFromFile 사용 (하드디스크 로컬디스크)
1-2. 에셋번들을 다운로드 하거나 패치해야 하는 경우(원격서버, 로컬서버)에는
버전 5.3 이상 AssetBundleDownloadHandler 사용 / 5.2 이하 WWW.LoadFromCacheOrDownload 사용
2. (1-2) 방법을 사용했을때 적절하게 Dispose 메소드를 호출했는지 확인
3. 에셋번들의 크기가 5MB를 넘지 않도록하는게 좋다.
4. 동시의 2개 이상의 에셋번들을 다운로드 하지 않는게 좋다. (async await 를 사용하기도 한다.)

4. 에셋번들에서 에셋 로딩하기

  1. 방법

    • LoadAsset
      -> 아래에 해당하지 않는 경우 사용

    • LoadAllAsset
      -> 에셋번들내 대부분의 오브젝트들을 로드 할때만 사용

    • LoadAssetWithSubAssets
      -> 애니메이션이나 스프라이트 아틀라스(다수의 스프라이트)를 사용하는 FBX 같은
      여러 내장 오브젝트를 포함하는 복합에셋인 경우 사용

      • 로드해야하는 에셋의 수가 많고 한번에 로드해야하는 숫자가 에셋번들이 가진 전체 콘텐츠의 2/3 미만인 경우 에셋번들을 여러개의 작은 번들로 분리하고 LoadAllAssets을 사용하는 것을 고려해 보는 것도 좋다.
      • Async를 붙이는 비동기와 안 붙이는 동기와의 메모리 효율성 차이점이 5.2 버전 이후 부터는 사라졌다. 상황에 맞춰 동기 비동기 사용하면 된다.
  2. 에셋번들 의존성

    • 부모에셋번들이 로드 될때 자식에셋번들을 자동으로 로드해주지않는다.
    • 1번 에셋보다 2번 에셋을 먼저 로드 하라는 말이 아니라 (자동으로 해주지도 않는다.)
      1번 에셋 A 오브젝트를 로드하기전에 명시적으로 B 머테리얼이 있는 2번 에셋을 로드해야된다.
  3. 에셋번들 매니패스트

    • 에셋번들을 빌드 할때 유니티는 각 에셋번들의 의존성 정보를 포함하는 오브젝트를 직렬화한다.
    • AssetBundleManigaest.GetAllDependencies
      -> 모든 의존성 정보 반환, 자식정보와 자식의 자식 등 정보 도 포함
    • AssetBundleManifest.GetDirectDependencise
      -> 바로 하위의 자식정보만 반환
  4. 권장사항

    • 사용자가 프로그램의 성능이 중요한 부분에 진입하기 전에 가능한한 많은 오브젝트를 로드 하는것이 좋다. (캐싱해 두는 것도 좋은 방법)

5. 설치 후 다운로드 (로드된 에셋 번들 관리)

  • AssetBundle이 LZMA 압축파일인 경우 향후 로딩속도를 높이기 위해서 LZ4로 압축 혹은 비압축상태로 캐시에 저장 된다.
  • 캐시가 가득차면 가장 오랫동안 사용되지 않은 AssetBundle을 캐시에서 제거한다.
  • 에셋번들을 로드하고 언로드 할 시기를 아는 것이 중요하다. (잘못 언로드 하면 오브젝트가 중복으로 저장되거나 텍스쳐가 누락되는 안좋은 상황이 발생한다.)
  • AssetBundle.Unload(bool value) 안에 true를 넣어 주는게 기본적으로 가비지가 생길 확률이 적다.(각 에셋종속성에 대해 잘 파악하고 있어야 한다.)

6. 중복

  1. 에셋 중복
    • 서로 다른 에셋 번들에 할당되어 있는 두 개의 오브젝트 모두 공통 종속성 오브젝트에 대한 레퍼런스가 포함되어 있는 경우, 해당 종속성 오브젝트가 두 에셋 번들에 모두 복사된다. (이렇게 되면 총 에셋번들의 크기가 커진다.)
    • 종속성을 공유하지 않도록 한다.(공유되는 종속성이 많은 프로젝트에서는 실용적이지 않다.)
    • 모든 종송된 에셋이 자체적인 에셋번들에 내장되어 있어록 한다. (중복될 위험은 사라지지만 복잡성이 발생한다.)
    • 에셋 번들에 할당되지 않은 종속성을 공유하지 않도록 보장하는 에디터 스크립트를 작성해서 모든 프로젝트에서 사용하는 것이 좋다.
  2. 스프라이트 아틀라스 중복
    • 동일한 스프라이트 아틀라스 안에 사용되는 스프라이트 들은 모두 동일한 에셋 번들에 할당해야한다.

7. 패치 시스템

  1. 패치 시스템에서 필요한 정보
    • 현재 다운로드된 에셋번들과 각각의 버전정보 리스트
    • 서버에 있는 에셋번들과 각각의 버전 정보 리스트
  2. 순서
    • 서버 측 에셋번들 리스트를 다운받고 로컬에 있는 에셋번들 리스트를 비교한다.
    • 버전정보가 변경된 에셋번들은 로컬에서 지운다.
    • 로컬영역에 없는 에셋번들을 다운 받는다.
  3. 방법
    • 에셋번들 파일리스트에 JSON 같은 표준데이터 포맷을 사용하고 체크섬 컴퓨팅에는 MD5같은 C#을 사용한다.

8. 캐싱

  1. UnityWebRequest API를 통해 다운로드 된 AssetBundle을 캐시하는데 사용 가능한 캐싱 시스템이 있다. (AssetBundle 내부 X, AssetBundle 시스템으로 생성 X)
  2. 마지막 번호를 계속 추척하며 번호가 일치하지 않거나 캐시된 AssetBundle 이 없으면 새로 다운로드 한다.
  3. 캐싱 시스템은 AssetBundle의 파일 이름으로만 식별되며 URL로 구분되지 않는다. (여러장소에 저장할 수 있다, 이름이 같으면 같은 AssetBundle로 인식한다.)
  4. AssetBundleManifest.GetAssetBundleHash() 사용은 추천하지 않는다. - 추정값을 제공할 뿐 실제 hash 연산을 하지 않는다.

9. 사용 방법

  1. UnityWebRequest.GetAssetBundle(String BundleURL, int version) 함수를 이용하면 이름 그대로 캐시로부터 로드 or 다운로드
    • 로드 = 프로젝트 내부에 url의 에셋번들과 이름이 같고 버젼이 같은 에셋번들이 존재
    • 다운로드 = 존재 하지 않으면 URL로 이동하여 에셋번들을 다운로드하여 캐싱
  2. 한번 더 UnityWebRequest.GetAssetBundle(String BundleURL, int version) 호출하여 캐싱된 에셋을 로드
    • LoadAssetSync(비동기)로 로드하면 여러 에셋을 동시에 로드할 수 있습니다.
      ex) 예제
      UnityWebRequest request = UnityWebRequest.GetAssetBundle(URL,0)
      AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
      bundle.LoadAsset("Cube");
      * 웹서버에 저장해둔 에셋번들의 링크를 BundleURL에 넣기

10. 쉽게 빌드 하는 방법인 Assetbundle Browser

  1. window -> Assetbundle Browser 를 킨다.
  2. Configure 탭을 눌러 사용할 에셋들을 드래그로 올린다.
  3. Build 탭을 눌러 Build Target을 플래폼에 맞추고 (현재 나의 사용 플래폼은 Standalone Windows 64)
    아래 Ouput Path 에 경로를 지정해준다. (현재 나의 경로는 Assets/_AssetBundle) Assets 폴더에 _AssetBundle 폴더가 없을 경우 알아서 생성된다.
  4. 아래 Build 버튼을 눌러준다. (경로에 파일을 생성한다.)
  5. 사용방법은 Configure로 돌아와서 오른쪽에 보면 [Asset] , [Bundle], [Size] 가 보이는데 이 string 정보를 토대로 스크립트에서 아래와 같이 적용하면 된다.
    AssetBundle bundle = AssetBundle.LoadFromFileAsync(에셋번들 경로 + [Bundle])
    //ex)에셋번들 경로 (Path.Combine(Application.dataPath, "_AssetBundle", [Bundle]))
    var asset = bundle.LoadAsset< 불러올 타입 ex) GameObject, Sprite 등 >([[Asset]]);
  6. 주의 사항으로는
    • AssetBundle.LoadFrodmFileAsync 이게 비동기로 이뤄지기 때문에 async await 를 사용하는걸 추천한다. (Async await Task 공부해보자)
    • 스크립트를 제외한 씬, 프리팹, 텍스쳐 등을 수정했다면 (스크립트도 public 연결등 무언가 바뀌었다면) AssetBundle Browser에 들어가서 다시 Build 버튼을 눌러줘야 바뀐정보다 저장이 된다.

참고 링크

유니티 메뉴얼 링크
https://docs.unity3d.com/kr/2018.2/Manual/AssetBundles-Browser.html

Comments