
에셋을 외부저장소에서 불러와 적용할 수 있게 해본다.
모바일 게임 프로젝트를 진행하면서 문제가, 구글 플레이 스토어에 게임을 올릴 때는 80메가 이하로 빌드 파일을 올려야 한다.
그런데, 게임에서 소스 코드가 차지하는 비율은 1메가가 안된다. 대부분의 용량은 에셋이 차지하기 때문에, 에셋을 게임에 접속했을 때 다운로드 하는 방식으로 구현하면 된다.
우리가 흔히 쓰는 에셋 적용 방식인 직접 레퍼런스 방식은 소규모 프로젝트 및 PC환경에서 적합하다.
Resources 폴더의 경우에는 간편하여 대부분의 프로젝트에서 사용하기 용이하다.
하지만, 사용에 주의를 요한다. string으로 파싱해서 찾는 방식이 때문에, 실수하기 쉽고 성능 상 좋지않다.
Addressable은 에셋 파일과 코드 파일들을 분리하여 빌드할 수 있게 해주는 기능으로, 직접 레퍼런스 방식과 Resources 폴더 방식 각각의 장점들을 합쳤다.
- 어드레서블 매뉴얼
- 어드레서블
- 어드레서블 에셋 시스템
- 에셋 시스템 소개 - Unite Seoul 2019
대략 9분 30초 쯤 부터 어드레서블이 나온다
리소스 폴더의 장점 + 에셋 번들의 장점 + 개발 편의성을 합친 것이 어드레서블 이다.
소규모, 단기간 프로젝트의 경우, 기존에는 에셋을 직접 레퍼런스 참조하여 쓰는 방법이었다.
중장기의 경우에는 Resources 폴더, 장기는 로컬 원격 에셋번들을 사용했다.

Resources 폴더에 있는 에셋을 실제 빌드에서 쓰지 않는 에셋도 빌드에 포함시키는 문제가 있다.
모든 에셋을 불러오는 로딩 과정이 있다. 그래서 오래걸린다.
에셋이 변경되는 부분이 있으면 빌드를 전부 다시 해야된다
에셋의 이름을 기준으로 로드하기 때문에, 에셋 이름을 변경하기 번거롭다. 빌드도 당연히 다시 해야한다.

게임 빌드와 에셋을 분리해서 관리하는 기능이다. 에셋을 번들(묶음) 여러 개로 관리한다. 필요에 따라 번들을 로드/언로드 한다. 이러한 방식을 통해 메모리를 절약한다.
번들 내의 에셋을 변경 시, 해당 번들만 신규 버전으로 업데이트 하면 된다.
에셋은 빌드와 별도로 관리되는 방식이기 때문에, 빌드 시간이 확 줄어든다. 빌드에 에셋이 포함되지 않기 때문. 같은 이유로 앱 시작 시간이 단축된다.
에셋 관리가 편리하다. 자유도가 부여된다.
단점은 아래와 같다.
번들끼리 연결되지 않도록 관리할 필요가 있다. 또는, 공통 번들이 먼저 로딩이 되도록 해야 한다.
코드들로만 모든 부분들을 관리해야 하다 보니, 러닝 커브가 생긴다.
위와 같은 다른 에셋 시스템의 단점들을 해결했다.
기능이 너무많다. 씨네머신과 비슷한 경우이다. 너무 많은 옵션에서 어떠한 기능을 써야할 지 파악하기 어려워진다.
즉, 러닝 커브가 높다. 매뉴얼과 샘플 씬들을 보면서 기능들을 익혀나가야 한다.
안드로이드 개발 전용으로 리패키징 된 어드레서블. 2023.1 버전 이상에서 지원한다.
어드레서블 기능에서 에셋 번들에 어떻게 에셋을 등록하는지 알아보자.

어드레서블을 처음 사용시 먼저 그룹을 만들어 줘야 한다.
Window -> Asset Management -> Addressables -> Groups 순으로 누르면 아래창이 뜬다.

처음 그룹을 만드는 경우에는 위와 같이 오른쪽 마우스 클릭을 통해생성 가능하다.

1. 그룹에 에셋을 추가하려면 단순하게 드래그&드롭으로 추가가 가능하다.

2. 각 프리팹의 인스팩터 창의 상단에 있는 Addressable을 체크하면 된다. 그룹을 선택할 수도 있다.

3. 프로젝트 창의 폴더를 기준으로 어드레서블의 그룹에 추가할 수도 있다. 씬 또한 가능하다.

이렇게 추가한 에셋은 이름을 원하는 대로 변경할 수 있다. 가독성을 고려하여 쉬운 이름으로 변경한다.

참조하는 프리팹은 이제 위치와 이름이 바뀌어도 그룹에서는 자동으로 값이 변경되어 적용되는 것을 확인할 수 있다.




간단하게 라벨로도 관리할 수 있다.
어드레서블로 지정한 에셋을 로드하는 방법을 알아본다.
흔히하는 방법의 로드 방법이다. 비동기 로드를 더 선호하는 편이다.
var prefab = Addressables.LoadAsset<타입>("Key");
Key에는 에셋 이름, Label등이 올 수 있다. 로드 시도 시, 다운이 안되어 있으면 다운로드를 진행 후 로드하는 방식이다.
// 첫 번째 방법
var prefab = Addressables.LoadAssetAsync<타입>("에셋이름").Completed += () => { 로직 수행 내용} // 또는 함수 추가;
// 두 번째 방법
var prefab = Addressables.LoadAssetAsync<타입>("에셋이름").WaitForCompletion();
Instantiate(prefab, location, rotation);
일반적으로 사용하는 방법이다.
Addressables.LoadAssetsAsync("Key",(result) => { 결과를 리스트로 담기 가능} );
AsyncOperationHandle<IList<타입>> resultList = Addressables.LoadAssetsAsync().WaitForCompletion();
바로 Instantiate로 불러올 수 있음
Addressables.InstantiateAsync();
씬 또한 어드레서블에 등록해서 로드를 할 수 있다. 씬매니저를 대신할 수 있다.
Addressables.LoadSceneAsync();
어드레서블을 쓰는 이유는 에셋을 관리하기 위해서다. 어떻게 에셋들을 외부에 관리하는지 보자.

종속성을 가진 것들은 자동으로 같이 빌드 때 에셋 번들에 포함된다. 안드로이드는 Build App Bundle을 체크해준다.


번들에 어떤 것들이 포함되는지 파악할 수 있다.
에셋 번들은 빌드 폴더에서 Library/com.unity.addressable/프로젝트명/빌드명 에 추가된다.
Window 환경의 경우, Windows -> StandaloneWindows64 폴더 안에 있다.

해당 에셋 번들은 빌드된 경로 폴더의 Streaming Assets에도 동일하게 있다.

빌드 캐시를 지울 경우, 위의 에셋 번들 또한 지워지는 것을 볼 수 있다.
메뉴판 같은거다. 에셋번들 안에 어떠한 에셋들이 포함되는 지 목록을 가지고 있다.

플레이어를 포함한 에셋 번들을 변경하고, 에셋번들만 빌드해본다. 기존의 Capsule이였던 플레이어 메시를 Cube로 변경 후, 빌드한 것을 위의 경로에 잇는 빌드폴더의 파일들을 교체 시 바로 적용된다. 즉, 새로이 전체 빌드를 할 필요가 없다.
새로운 에셋번들 전의 번들과 교체해보면 빌드는 새로 뜨지 않았는데도 불구하고, 교체된 에셋번들이 적용되는 것을 알 수 있다. 이러한 특징의 장점은 아래와 같다
이를 통해 빌드 과정에서 에셋 빌드는 빼고, 스크립트 빌드만 하게 되기 때문에, 빌드 시간이 매우 빨라진다.
나중에 Ci/Cd를 배우면, 원클릭으로 딸깍으로 게임 배포가 가능해진다.
구글 클라우드 스토리지의 버켓과 연결해서 원격 컨텐츠 배포를 구현하도록 한다. 순서대로 진행한다.
Content Delivery Network
Cloud Content Delivery(Unity)
플레이 스토어에는 빌드 파일을 올리고, 에셋 다운로드는 딜리버리 서비스를 이용한다.
기업에서는 보통 AWS를 이용한다. 이번에는 구글 클라우드를 이용한다.
먼저, 구글 클라우드 아이디를 만들도록 한다. 체험판으로 300달러를 사용할 수 있다. 300달러를 다 쓸 때쯤에 다른 아이디로 옮겨가면 된다. 버킷을 만들면 된다.
에셋을 저장하는 버킷을 하나 만든다. 여기에 우리의 에셋들이 저장될 것이다. 공개 액세스 방지 설정은 해제한다.
- 구글 드라이브는 파일마다 주소가 있어서 쓸 수 없다.
- 파이어베이스의 스토리지는 유료가 됐다.


해당 버켓에 접근하는 모든 이들이게 뷰어 권한을 준다.
여기서 gsutil url을 복사해간다. 그런데, 어드레서블의 경우 gs로 시작하는 프로토콜을 지원하지 않는다. 아래의 주소로 변경해서 가져가야 한다.
https://storage.googleapis.com/{버킷명}/[BuildTarget]
게임 시작할 때 마다 다운받는 것은 너무 부담이 될 것이다.
그런데, 빌드 파일 실행 시 맨 처음에만 다운을 받자니 새로운 업데이트가 있을 때 마다 변경된 부분들을 받아야 한다.
이러한 변경사항(추가, 수정, 삭제)들은 어드레서블에서 구현이 되어 있다. 우리는 번들 변경사항 체크만 하면 된다.

간단하게 이미지를 테스트해본다. 라벨을 IMG로 해둔다. 에셋 번들명은 Images다.

체크한 부분대로 바꿔준다. 버킷명 뒤에 [BuildTarget]을 추가하면 된다.


Build Remote Catalog 체크 후, Build&Load Paths를 Remote로 변경한다. Load Path가 제대로 되어 있는지 확인한다.
게임에 포함되는 에셋들의 묶음인 에셋 번들을 설정한다. Packed Asset을 클릭하면, 프로젝트 창에서 자동으로 해당 에셋 번들의 SO위치를 알려준다.

Build&Load Paths 방식을 Remote로 변경한다.

해당 경로를 통해 에셋 번들을 빌드할 수 있다.

딱 세 종류의 파일만 올리면 된다.
경로는 앞서 봤던 BuildPath에 있다. 현재의 경우는 아래와 같다.
프로젝트 경로/ServerData/플랫폼(안드로이드)
해당 파일을 업로드 한다.
public class Tester : MonoBehaviour
{
[SerializeField] private Image[] image;
[SerializeField] private TextMeshProUGUI progressText;
[SerializeField] private Slider progressBar;
private List<Sprite> sprites = new();
private AsyncOperationHandle<IList<Sprite>> spriteList;
private Sprite[] testSprite = new Sprite[4];
private async void Start()
{
foreach (var locator in Addressables.ResourceLocators)
{
foreach (var key in locator.Keys)
{
Debug.Log("✅ Addressable Key: " + key);
}
}
await LoadAssets();
}
private async Task LoadAssets()
{
sprites.Clear();
spriteList = Addressables.LoadAssetsAsync<Sprite>("IMG", (result) => { sprites.Add(result); });
while (!spriteList.IsDone)
{
float progress = spriteList.GetDownloadStatus().Percent;
UpdateProgressUI(progress);
await Task.Yield();
}
spriteList.Completed += (result) =>
{
testSprite[0] = sprites[0];
testSprite[1] = sprites[1];
testSprite[2] = sprites[2];
testSprite[3] = sprites[3];
image[0].sprite = testSprite[0];
image[1].sprite = testSprite[1];
image[2].sprite = testSprite[2];
image[3].sprite = testSprite[3];
};
}
private void UpdateProgressUI(float progress)
{
progressText.text = $"Progress: {progress:P}";
progressBar.value = progress;
}
}

게임 출시 후, 사전 다운로드를 통해 에셋을 한 번에 로컬에 저장한다. 이후, 버전 변경을 그때그때 체크해서 달라진 에셋이 있을 경우 알아서 로드한다.
크게 4가지 스탭이 필요하다.
