Unity 에셋 번들은 게임의 리소스를 효율적으로 관리하고 최적화하는 데 중요한 역할을 한다. 특정 리소스를 에셋 번들로 묶어 필요할 때만 다운로드하고 로드함으로써 메모리 사용을 줄이고 게임 로딩 속도를 개선할 수 있다. 현업에서 중요한 기술 중 하나이므로 에셋 번들에 대해 알아보도록 하겠다.
에셋 번들(Asset Bundle)이란?
에셋 번들: 여러 에셋들을 하나로 묶어 주는 파일 포맷
유명한 모바일 게임들을 다운해본 적이 있는가?
이때 바로 플레이가 가능하지 않고 패치가 시작된다.
그 이유는 에셋 번들을 다운로드하기 때문이다.
에셋 번들 없이 게임을 빌드한다면?
게임 용량이 엄청 커지고, 에셋이 변경될 때마다 매번 에셋 전체를 다운받아야 해서 비효율적이다.
그래서 모바일 게임은 에셋 번들이 필수이다.
에셋 번들 빌드 파이프라인
① Incremental Build
- 중복 에셋 체크 및 제외 처리
- 예를 들어, 1.0.0v에서 사용된 에셋은 제외하고 1.0.1v에서 새롭게 추가된 에셋만 빌드
② Build Player Scripts
- 플레이어 스크립트 빌드
- 스크립트는 에셋 빌드에 포함되지 않기 때문에 따로 빌드
아래 작업부턴 모든 번들에 대해 각각 수행
③ Calcuate Dependencies
- 에셋 번들 간의 의존성 체크
④ Write Bunddle
- 에셋들을 묶어 에셋 번들 제작
⑤ Archive & Compress Bunddle
- 에셋 번들 압축
- 압축 방식은 LZMA, LZ4, 비압축 방식이 존재
압축 포맷 | 내용 | 특징 |
LZMA | 기본 압축 포맷, 직렬화 스트림 기반 | - 압축률 가장 좋음 - 사용 전에 전체 압축을 해제하기 때문에 로드가 오래 걸림 |
LZ4 | 청크(Chunk) 기반, Slotted Page 자료구조를 사용 | - 압축률이 LZMA보다 떨어짐 - 사용 전에 전체 압축을 풀 필요 없이, 해당 에셋에 대한 번들 청크만 압축을 해제하기 때문에 로드가 빠름 |
비압축 | 압축을 하지 않음 | - 압축을 하지 않아 크기가 가장 큼 - 접근이 가장 빠름 |
⑥ Create AssetBundleManifest
- 에셋 번들 Manifest 생성
- Version, Hash, Type, Asset, Dependencies 등 정보가 포함됨
여기까지 한 에셋 번들이 만들어지는 과정
다시 ③으로 돌아가 다음 에셋 번들에 대해 처리
⑦ Create All AssetBundleManifest
- 모든 에셋 번들에 대한 Manifest를 생성
❓ Manifest가 뭔가?
✅ Manifest는 에셋 번들의 메타데이터 파일이다. 각 에셋 번들에 포함된 리소스의 경로, 버전, 의존성 정보 등이 기록되어 있다. 이를 통해 Unity가 에셋 번들을 효율적으로 로드하고 관리한다. 예를 들어, 특정 에셋 번들이 다른 번들에 의존하는 경우 Manifest 파일의 의존성 정보에 따라 필요한 번들을 함께 로드한다.
에셋 번들 구조
에셋 번들의 자료구조는 Slotted Page에 기반한다.
Header 부분을 보면 Lookup Table이 존재한다.
Lookup Table은 압축된 Asset을 가리키는 포인터를 담고 있다.
이때 Lookup Table은 이진 탐색 트리를 사용한다.
💡 Window, OSX, iOS는 레드 블랙 트리 사용
❓ 위 구조는 LZMA, LZ4에 모두 사용되는건가?
✅ 아니다. 위 구조는 LZ4 압축 방식에 해당한다. LZMA는 비트 단위의 압축 방식을 사용하며, 전체 데이터의 흐름을 하나의 연속적인 스트림으로 압축하기 때문에 위와 같은 구조는 사용하지 않는다.
에셋 번들 의존성
에셋 번들의 가장 큰 문제 중 하나는 의존성(Dependencies)에서 발생한다.
개발자가 에셋들을 A, B, C (혹은 그 이상)의 에셋 번들을 빌드했다고 가정하자.
- A 에셋 번들의 객체가 B와 C 에셋 번들의 객체를 참조
- A는 B와 C 간의 의존성 발생
- 즉, B와 C는 A 에셋 번들이 반드시 필요 (의존성 발생)
이러한 의존성 문제에 대한 해결 방법은 뒤에서 정리하도록 하겠다.
에셋 번들 워크플로우
에셋번들 워크플로우
① 빌드한 에셋 번들을 CDN(파일 저장 서버 및 클라우드)에 업로드
② CDN에 UnityWebRequest를 요청하여 다운받아 캐싱 ( 캐싱이 되면 다시 실행할 때는 다운할 필요가 없다. )
③ 클라이언트에서 에셋 번들을 에셋으로 로그하여 나눠 사용
💡 이때, 에셋 번들의 Version을 체크하여 변경사항이 있다면 패치 진행
에셋 번들 그룹화 이론
그렇다면 유니티에서 어떤 기준으로 에셋 번들을 나누면 좋을까?
아쉽게도 에셋 번들을 그룹화 하는 방법에 정답은 없다.
대신, 에셋 번들을 그룹화하는 Tip은 존재한다.
① 논리적 그룹화
- UI, 캐릭터, 환경 등 논리적 그룹화 수행
- DLC에 적합한 방식
- 언제 어디서 사용될 지 정확히 알고 있어야 함
② 종류별 그룹화
- 에셋 타입 별로 그룹화 수행
- 예를 들어, 오디오 트랙, 쉐이더, 메터리얼 등
최적의 에셋 번들의 개수
에셋 번들의 개수는 너무 작아도 문제고 너무 많아도 문제다.
그래서 그룹화를 할 때 적절한 개수로 묶는 것이 중요하다.
① 너무 적은 에셋 번들을 가진 경우
- 실행 시 메모리 사용량 증가 (많은 양의 에셋이 묶여 있기 때문)
- Lookup Table이 커지며, 에셋 번들의 헤더는 게임 최초 실행 시 메모리에 올라오기 때문에 메모리 사용량이 증가
- 로딩 시간이 증가하고 업데이트 시 추가 다운로드 양이 증가
② 너무 많은 에셋 번들을 가진 경우
- 빌드 파이프라인은 에셋 번들의 개수만큼 Loop을 돌기 때문에 빌드 시간이 증가
- 에셋 번들의 의존성이 증가할 수 있음
- 에셋 번들의 헤더의 수가 증가하므로 전체 다운로드 시간이 증가
결론은 프로젝트마다 적절히 그룹화 하는 것이 좋다.
📌 추가팁
- 자주 변경되는 것과 변경되지 않는 것을 분리
- 모델과 연관된 텍스처와 애니메이션을 그룹화 (의존성 제거)
- 여러 에셋 번들이 참조하는 에셋은 공용 에셋 번들로 이동
- SD/HD 에셋처럼 절대 동시에 로드하지 않는 에셋 그룹화
- 따로 로드되는 경우 분리 고려
- 항상 같이 로드되는 경우 통합 고려
- 같은 그룹인데 버전만 다른 경우 AssetBundle Variants 고려
💡 AssetBundle Variants
지정한 플랫폼에 맞는 에셋 번들을 선택 가능하도록 한다.
예를 들어, Window는 HD 에셋번들을, 안드로이드는 SD 에셋 번들을 취사 선택하여 다운한다.
혹은 하드웨어의 특정(화면 비율)에 따라 Variant 구성 가능하다.
제한 사항: 각각의 에셋 파일이 존재해야 함 (그래서 업데이트마다 모든 Variants 추가 및 관리 필요)
에셋번들 장단점
에셋 번들 장점
① 앱 용량 감소
② 리소스 업데이트 진행 용이
③ 전체 재설치 없이 에셋 번들만 추가로 다운로드 진행
에셋 번들 단점
① 에셋 번들 그룹화 과정 필요
② 에셋 번들 빌드하는 작업 필요
③ 설치 후 에셋 번들을 체크하고 다운로드하는 작업 필요
④ 메모리를 관리 추가 작업 필요
⑤ 에셋 번들을 통해 생성된 게임오브젝트는 파괴되더라도 메모리는 사라지지 않기 때문에 언로드 작업이 추가로 필요
❓ 에셋 번들로 만들 수 있는 에셋의 범위는?
✅ 대부분을 에셋 번들로 만들 수 있다. 씬 통째로 에셋 번들로 구성 가능하다. (스크립트 및 프로젝트 세팅 같은 것들은 제외)
정리
에셋 번들은 모바일 게임 혹은 실시간 서비스 게임에서 필수적이다.
오늘은 이론을 살펴봤으니 다음에는 실전으로 넘어가 에셋 번들을 다루도록 하겠다.
참고 자료
https://youtu.be/Lx61ZEKEvnQ?si=9Zw3rqEQBQmbkBKO
'유니티(Unity) > 이론 정리' 카테고리의 다른 글
[Unity Korea] 객체 지향 설계 원칙 (SOLID) (0) | 2024.11.12 |
---|---|
클래스 이름 짓기 [ 2편 ] + MVC 패턴 (2) | 2024.11.11 |
클래스 이름 짓기 [ 1편 ] (1) | 2024.11.10 |
싱글톤(Singleton) 패턴 (0) | 2024.03.22 |
GetComponent 성능 (1) | 2024.03.16 |