Chapter 2. Unity Cloud를 이용한 Addressable

개발하는 운동인·2025년 11월 4일

🌟 Addressable

  • 에셋 번들의 편의성을 위한 유틸리티

⭐ 에셋 관리를 할 때, 제일 중요한 포인트가 Asset의 경로(위치)이다.

  • 에셋이 Resources 폴더에 있는지
  • 에셋이 로컬의 Asset Bundle에 있는지
  • 에셋이 서버의 Asset Bundle이 있어서, Remote로 받아야 되는 상황인지?

🤔 왜 중요할까?

  • 프로그래머가 경로에 따라 코드를 바꿔야 한다.
  • 에셋이 Resources 폴더에 있다면, Resources.Load 코드 사용
  • 에셋이 서버의 Asset Bundle로 있다면, 웹리캐스트로 날려서 다운로드 받아야 한다.
  • 개발 초기 단계(프로토 타입 만드는 과정)에서는 처음부터 Asset Bundle을 만들어서 사용하진 않는다. 예를 들어, Asset Bundle을 서버에 올려놓고 쓰지 않는다.
  • 이유는 그걸 구축하는데 시간이 오래걸린다. 빠른 프로토타이핑을 하기 위해서 Resource폴더에 다 때려 박는다.
  • 프로토타입 이후 Resource 폴더에 있었던 에셋들을 분리해서, Asset Bundle로 빼내기도 한다.
  • 자꾸 에셋 위치를 바꿀 때마다, 프로게이머는 코드를 변경해야 한다.

⭐ 개념

  • Addressable은 오브젝트의 위치만 알고 있으면 된다. 알고 있으면 항상 접근 가능하다.

  • 각 에셋 별로 Addressable을 줄지 안줄지 선택 가능하다. 위 오브젝트 사진 참고

  • 에셋에 Addressable을 주게 될 경우, 해당 에셋은 Addressable 시스템을 갖게 된다.

  • 에디터에서 AssetGroup의 빌드(컴파일) 하게 되면 Addressable 에셋들의 각각 AssetBundle을 만들어지게 된다.

  • 디바이스(모바일)에서는 AssetBundle을 요청할 때, 카탈로그와 프로바이더가 들어가게 된다. 내가 요청하는 에셋과 Addressable의 매핑 정보이다. 즉, Address를 가지고 요청을 해도 해당 에셋을 정확히 로드할 수 있게 된다.

⭐ 카탈로그와 프로바이더 설명

⭐ 2019버전 Addressable

  • 각 Addressable에 대해서 그룹화를 할 수 있다.
  • 프로파일을 만들 수 있다.

⭐ 프로파일

    1. Edtior Hosted : 리소스 폴더에 에셋이 있다는 것을 감안하고 플레이 해보고 싶은 것.
    1. Remote Hosted : 이 에셋이 Remote에 있어서 Asset Bundle을 다운로드 받아서 실행하는 것처럼 테스트 해보는 것

⭐ Addressables로 관리

개념

  • 개발자가 게임 장면에 객체인스턴스화하는 방법을 더 많이 제어 할 수 있는 매우 강력한 기능

  • 이는 대규모 세계에서 성능을 개선하고, 메모리를 절약하는데 유용할 수 있지만, 개발자가 클라우드 서버에서 게임 객체를 게임에 로드할 수 있다는 것. 즉, 게임 다운로드 크기가 작아지고 대상 사용자에게 소스가 더 빠르게 전달됨.

  • 또한, 새 업데이트를 푸시할 필요 없이 게임에 동적으로 추가할 수 있다.



✅ 기본적인 객체 인스턴스화 방법

public class AddressbleObject : MonoBehaviour
{
    //1. 기본적인 객체 인스턴스 방법

    [SerializeField]
    GameObject Map;

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.I))
        {
            Instantiate(Map);
        }
    }

⭐ 현재 프로젝트 파일 크기

  • 25 GB로 어마무시한 용량이다.

⭐ 위 맵 에셋을 Addressable로 관리하려면?

    1. 아래 패키지 다운로드
    1. 아래 Groups 클릭
  • 아래 창은 Addressable에서 사용해야 하는 프리펩이나 에셋들을 저장하는 장소이다.

    1. 맵 에셋을 드래그 하여 추가한다.
  • 추가하게 되면 Addressable이 체크 된 것을 볼 수 있음.

👉 Addressable 인스턴스화 전용 타입 선언

    1. 기존 코드를 주석하고 에셋 참조 게임 객체 타입으로 선언하는 코드 작성
  • 해당 맵 프리펩 할당까지 진행

👉 Addressable 인스턴스화 방법 - 1

using System.Runtime.InteropServices;
using Unity.VisualScripting;
using UnityEditor.EditorTools;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

// Used for the Hat selection logic
public class AddressbleObject : MonoBehaviour
{
    //1. 기본적인 객체 인스턴스 방법

    //[SerializeField]
    //GameObject Map;

    //private void Update()
    //{
    //    if(Input.GetKeyDown(KeyCode.I))
    //    {
    //        Instantiate(Map);
    //    }
    //}

    //2. Addressable

    [SerializeField]
    AssetReferenceGameObject DokyoLobbyMap; //⭐0. 에셋 참조 게임 객체 타입으로 선언

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.I))
        {
            //1번째 방법 : async(비동기)를 선언하여 에셋을 Ram에 로드하는 것
            DokyoLobbyMap.LoadAssetAsync().Completed += OnAddressableLoaded; //⭐1. 에셋 로딩이 완료되었음을 알 수 있도록 Action 델리게이트 선언
        }
    }

    private void OnAddressableLoaded(AsyncOperationHandle<GameObject> handle) //⭐2. 매개 변수로 게임 객체 유형의 비동기 작업 핸들을 받아옴
    {
        if(handle.Status == AsyncOperationStatus.Succeeded) //⭐3. 비동기 작업 상태(로딩 프로세스) 가 성공적이라면
        {
            Instantiate(handle.Result); //⭐4. 로딩 프로세스의 결과를 인스턴스화
        }
        else
        {
            Debug.LogError("해당 에셋의 로딩이 실패하였습니다.");
        }
    }
}

실행 시, 인스턴스화 된다.

👉 Addressable 인스턴스화 방법 - 2(깔끔해진 코드)

using System.Runtime.InteropServices;
using Unity.VisualScripting;
using UnityEditor.EditorTools;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

// Used for the Hat selection logic
public class AddressbleObject : MonoBehaviour
{
    //1. 기본적인 객체 인스턴스 방법

    //[SerializeField]
    //GameObject Map;

    //private void Update()
    //{
    //    if(Input.GetKeyDown(KeyCode.I))
    //    {
    //        Instantiate(Map);
    //    }
    //}

    //2. Addressable

    [SerializeField]
    AssetReferenceGameObject DokyoLobbyMap; //에셋 참조 게임 객체 타입으로 선언

    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.I))
        {
            //⭐ 2번째 방법 : Addressable가 관리하는 에셋들은 특별한 게임 객체들이므로, 로딩과 인스턴스화를 동시에 처리할 수 있음
            DokyoLobbyMap.InstantiateAsync(); //⭐ 동시에 비동기 로딩과 인스턴스화를 동시에 처리
        }
    }

}

실행 시, 정상적으로 인스턴스화 된다.


⭐ 서버에서 Addressable의 에셋들을 빠르게 로드하는 두 가지 팁

⭐ 인스턴스화 된 Addressable 올바르게 삭제

using System.Runtime.InteropServices;
using Unity.VisualScripting;
using UnityEditor.EditorTools;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

// Used for the Hat selection logic
public class AddressbleObject : MonoBehaviour
{
    //2. Addressable

    [SerializeField]
    AssetReferenceGameObject DokyoLobbyMap; //에셋 참조 게임 객체 타입으로 선언

    GameObject instanceReference; //⭐ 0. 인스턴스화 된 오브젝트에 대한 참조(주소)를 저장할 게임 오브젝트 변수 선언
    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.I))
        {        
            DokyoLobbyMap.InstantiateAsync().Completed += OnAddressableInstantiated; //⭐ 1. Addressable의 에셋 인스턴스화가 완료되면 OnAddressableInstantiated 호출
        }
        else if(Input.GetKeyDown(KeyCode.D))
        {
            DokyoLobbyMap.ReleaseInstance(instanceReference); //⭐ 5. OnAddressableInstantiated 메서드에서 로딩 프로세스(Addressable의 에셋 인스턴스화) 의 결과를 저장한 오브젝트 메모리 해제
        }
    }

    //올바르게 Addressalbe로 관리 중이였던 에셋들을 삭제 하기 위한 메서드 선언 -> 실질적인 삭제는 아님. 삭제를 하기 위한 메서드라고 생각하면 됨.
    private void OnAddressableInstantiated(AsyncOperationHandle<GameObject> handle) //⭐ 2. 매개 변수로 게임 객체 유형의 비동기 작업 핸들을 받아옴
    {
        if (handle.Status == AsyncOperationStatus.Succeeded) //⭐ 3. 비동기 작업 상태(로딩 프로세스) 가 성공적이라면
        {
            instanceReference = handle.Result; //⭐ 4. 인스턴스화된 오브젝트의 주소를 오브젝트 변수에 저장
        }
    }
}

실행 시, Addressable의 인스턴스화 및 삭제 과정(메모리 로드 및 언로드)에서 메모리를 20MB밖에 사용하지 않는 것을 볼 수 있다.

  • Addressable 인스턴스화

  • Addressable 인스턴스화 된 오브젝트 삭제(메모리 해제)

⭐ AssetReferenceGameObject 는 파생 클래스이며, 다른 파생 클래스도 존재하고, 자산 참조 클래스 존재한다.

  • AssetreferenceAudioClip 클래스는 다음과 같은 오류가 나타난다.
  • 즉, AssetReference의 기본 클래스 중 AssetreferenceAudioClip 클래스가 파생클래스가 아니므로 다음과 같은 정의 오류가 나타난다.

🤔 해결하기 위해서는?

    1. 자산 참조 클래스 T (AssetReferenceT)에서 파생된 고유한 클래스로 상속받게 한다.

      - 하지만 그래도 오류가 나타난다.
    1. AssetreferenceAudioClip에 마우스 갖다대고, 아래 생성자를 생성
    1. 마지막으로, Unity Inspector(검사기)에 노출될 수 있도록 직렬화하는 코드를 작성하여 오디오 파일을 드래그 앤 드롭할 수 있게 한다.

인스펙터 화면

⭐ 서버에서 Addressable 에셋을 로드하는 방법

  • 유니티에서 이 작업을 쉽게 수행할 수 있으며, 코드를 수정하지 않고도 작동하도록 할 수 있다.

    1. 패키지매니저에서 Addressable 를 다운로드하면, 에셋 폴더에 AddressableAssetData가 생기게 되는데, 아래 AddressableAssetSettings을 클릭한다.
    1. Bulid Remote Catalog 속성을 활성화 한다.

  • “Build Remote Catalog = 게임 클라이언트 업데이트 없이, 서버에서 최신 리소스를 내려받아 쓸 수 있게 만드는 기능” 이다.

    1. Buld & Load Paths 속성을 Remote로 설정

  • 최신 카탈로그를 서버에 업로드 할 수 있게 활성화 하였으니, 에셋 실제 저장 경로와 런타임에서 게임이 에셋을 어디서 불러올지 저장하는 경로를 원격에서 받아 올 수 있게 한다.

    1. 스크롤 하다보면, 아래처럼 패키지된 자산을 관리하는 에셋이 나온다.
  • Packed Asset에서 Build Path와 Load Path를 각각 Remote.BuildPath와 Remote.LoadPath로 설정 한다.


  • 즉, 에셋들을 서버에 올리고, 실행 시 서버에서 다운로드하여 사용

    1. 아래 속성은 체크하지 않는다. 이 속성은 : 출시 이후에 리소스의 변경점을 바꾸고 싶다면 새 리소스를 업로드하여 기존 것을 교차하는 기능이다.

  • 유니티 6 이전 버전에서는 아래와 같이 세팅을 하는게 맞는데, 유니티 6버전에서는 체크하지 않는다. 즉, Prevent Updates 체크 = 기존 Can Change Post Release 체크 해제 상태와 동일하게 동작합니다.

    1. AssetGroups 폴더에 Defalut Local Group 클릭
  • Defalut LocalGropu의 Build Path와 Load Path를 각각 Remote.BuildPath와 Remote.LoadPath로 설정 한다.

    1. 아래 에셋도 6번처럼 똑같이 설정한다.

    1. AddressableSettings 클릭
  • Manage Profilles 클릭

  • 그럼 아래와 같이 창이 뜨는데, Remote.LoadPath 경로에 현재 프로젝트 대쉬보드 경로를 복사하여 작성한다.

    1. Cloud Content Delivery 클릭
  • Lanch 클릭 시, 50 GB 까지 무료이고 그 이후부터 유료로 결제해야 한다.

  • CCD (Cloud Content Delivery) 에서 GB가 오르는 조건

  • GB 계산 방법

  • 유저가 게임을 실행하고 나서, Addressable에 등록된 에셋들을 볼 때 GB가 올라감.

  • 번들 크기 보는 방법

  • 에디터 모드에서 GB 영향 없음

  • Unity Remote 상관없음

    1. 9번에서 구매하러 가기 버튼을 누른 뒤, 새로운 버킷을 추가한다.

✅ CCD - Bucket

  • Unity CCD(Cloud Content Delivery) 에서 말하는 버킷(Bucket) 은 콘텐츠를 묶어두는 저장소 단위예요.

  • 쉽게 말하면 파일(에셋 번들, Addressables 빌드 결과 등)을 올려두는 ‘컨테이너’ 라고 생각하면 돼요.

  • 아래처럼 내용들을 적고 Next버튼 클릭

  • 위 버킷 유형은 2가지로 나뉜다.

  • 일단, 일반 버킷 기준으로 Addressable 세팅을 하였기에 일반 버킷으로 진행 후 개발 막바지때 프로모션 버킷으로 변경할 것이다.

    1. 아래 처럼 새 버킷이 만들어졌을 것이다.
  • 아래에 내가 추가하고 싶은 에셋 파일들을 올리면 된다.

  • 하지만, 먼저 Unity에서 먼저 빌드(컴파일)을 하고 빌드 결과물을 버킷에 업로드 해야 한다.

⭐ Addressable 워크플로우 최적화 방법

Addressable 자산 번들로 변환하지만, Addressable을 컴파일 하기 전에, 전체 환경을 일부 작은 속성으로 바꿔서 서버에 대용량 파일을 업로드 하지 않아도 되도록 할 것이다. -> 의미 해석

  • 위 사진 1번 의미.

컴파일 결과 전, 로컬에서 인스턴스화 확인 : 정상 작동

    1. 로컬에서 정상 작동했으니, 이제 빌드 해본다. 아래 처럼 Addressables Groups에서 Defalut Build Script 클릭 -> 컴파일
  • 컴파일 도중 에러 발생

  • AddressableObject 스크립트에서 아래 using문이 컴파일 에러를 발생시켰다. 코드 삭제.

  • 다시 컴파일 해본다. 결과는 성공적이다.

    1. 프로젝트 폴더에서 ServerData 폴더가 생겼을 것이다. ServerData -> Android(현재 프로젝트 플랫폼이 명시됨)

⭐ 에셋 번들 크기 -> Addressable 컴파일 시 나타남

    1. 내가만든 CCD의 버킷에 Android 안에 있는 파일들을 업로드를 한다.
  • 아래 업로드 버튼 클릭

  • 업로드한 파일들이 모두 완료 되면, 새로고침한다.

⭐ 버킷에 파일 올리고 나서, Release 진행

    1. Release 버튼 클릭
  • Relese 단계 모두 거치고, Submit 버튼 클릭

  • 프로모션 버킷 또는 실제 서비스 환경에서는 버킷 업로드만 한다고 유저가 바로 다운로드를 할 수 있는 건 아니다. Release를 해야 Catalog가 활성화되고, 유저가 해당 Asset Bundle을 다운로드 할 수 있다.

  • Release를 해야 공식 배포용으로 지정된다.

  • 일반 버킷에서는 Release는 불 필요하고, Local Build Path나 Regular Bucket에서 테스트 할 때는 안해도 바로 로드 가능하다.

    1. 마지막 Relase 버전의 URL 복사한다. (Badge:latest를 가리킨다), 다른 URL은 특정 릴리스만 가리킨다.

    1. Addressable Asset Settings -> Manage Profilles -> Remote.LocalPath에 복사한 URL을 붙여넣기 한다.

⭐ CCD + Addressable 시스템의 장점(Addressable 컴파일 -> 버킷 파일 업로드 -> Relase -> 해당 Relase URL 복사

⭐ Addressable Build 전 깨끗한 빌드 환경 확보

  • 난 DokyoCityLobbyMap 프리펩을 Addressables Groups에 추가했었었다.

  • 아래 사진은 해당 프리펩인데, 프리펩 자식에 있는 표지판을 위치나 회전 등 편집을 하고,

  • 또 다른, 프리펩을 Addressables Groups에 추가했다.

  • 추가하고 나서, 빌드(컴파일)을 해야 하는데, 그 전에, 이전 SeverData 폴더 - Androiod 에 있는 파일들을 삭제 하고, 컴파일을 해야 한다.

⭐ 새 빌드를 만들고, Addressables Groups를 빌드(컴파일) 하는 것이 아니라, 컴파일 없이 해당 프리펩에 편집한 내용이 있으므로, 이전 빌드를 업데이트만 할 것이다.

    1. 아래 Update a Previous Bulid 클릭
    1. Continue 클릭

이 과정은 Unity6 이전 방식이다. Unity6에서는 2번 과정에서 알아서 처리 된다. 내 현재 프로젝트는 Unity6 이므로, 따로 하진 않았다.

    1. 프로젝트 폴더 -> Asset -> AddressableAssetsData -> Android (현재 프로젝트 플랫폼)
  • 아래 파일 클릭.

    1. Remove All Entries 클릭
    1. 다시 ServerData -> Android에 들어있는 파일들을 버킷에 업로드
  • Addressables에 더 많은 콘텐츠를 추가했기 떄문에, 업로드하는데 시간이 더 오래 걸린다. 모두 업로드가 되었다면 당연히 Release 진행

    1. Release 진행
  • 빌드하게 되면 내가 추가한 프리펩 편집한 내용처럼 프리펩을 볼 수 있을 것이다.

0개의 댓글