본문 바로가기

공부/유니티

[Unity] Addressable Asset System

목차
  1. 서론
  2. 리소스 폴더
  3. 에셋 번들
  4. 어드레서블
  5. 어드레서블 사용 방법(기본)
  6. 웹 서버를 사용하여 어드레서블 적용하기
  7. 어드레서블 설정

 

어드레서블은 프로젝트의 에셋을 효율적으로 관리하고 로드할 수 있게 해주는 시스템이다.

 

이렇게만 말하면 어디에 사용되는지 예상가지 않을 것 같아서 몇 가지 사용 방식을 예시로 들어보았다.

  • 모바일 스토어 용량 등의 이슈를 해결하기 위한 추가 다운로드 및 패치
    바람의 나라:연 추가 다운로드
  • DLC를 통한 새로운 콘텐츠 추가
    젤다의 전설 DLC

어드레서블이 나오기 전에는 리소스 폴더나 에셋 번들을 사용하여 에셋을 관리했었다.


리소스 폴더

프로젝트 내에 특정 폴더를 리소스 폴더로 지정하면 해당 폴더 내의 에셋을 동적으로 로드할 수 있다.

using UnityEngine;

public class TistoryAddressable : MonoBehaviour
{
    void Start()
    {
        GameObject res = Resources.Load("TistoryResource") as GameObject;
        Instantiate(res);
    }
}

 

다음과 같이 Resources 폴더 내에 에셋을 넣어놓고, 에셋의 이름으로 리소스를 로드할 수 있다.

보이는 것과 같이 사용이 매우 간편하다.

 

리소스 폴더의 단점

  • Resources.Load 메소드는 Object 타입으로 반환되므로, 적절한 타입으로 캐스팅이 필요하다.
  • 문자열을 통해 불러오기 때문에, 이름이나 경로가 변환되면 하나하나 수정해줘야한다.
  • 리소스 폴더에 있는 에셋들은 빌드 시점에 모두 로드되어야 하기 때문에 초기 로딩 시간이 길어진다.
  • 또한 미리 로드되어 있기에 런타임 메모리의 성능이 줄어든다.

따라서 이러한 단점들을 보완할 수 있는 에셋 번들에 대해 알아보자.


에셋 번들

에셋 번들은 주로 런타임에서 추가 콘텐츠를 다운로드할 때 사용되며, 리소스 폴더의 단점을 극복한 방법 중 하나이다.

 

에셋 번들의 특징

  • 에셋 번들은 여러 에셋을 하나의 묶음으로 만드는데,
    이렇게 묶인 에셋 번들은 빌드 시점에 패키징되어 믈리적인 파일로 저장된다.
  • 런타임에서 필요한 시점에 동적으로 로드할 수 있기 때문에, 초기 로딩 시간을 줄일 수 있다.
  • 물리적인 파일로 저장된 에셋 번들을 AWS 같은 원격 서버에 저장하고, 게임 실행 중에 동적으로 다운로드 할 수 있다.
    원격 서버에 저장하면, 빌드 사이즈를 줄일 수 있어 모바일에 
  • 특정 플랫폼에 대한 에셋 번들을 따로 빌드할 수 있다.

 

에셋 번들의 단점

  • 어려움. 초기 설정이 복잡하고, 잘 계획해서 안짜면 메모리 손해본다.
  • 에셋 번들을 사용하는 과정에서 경로나 이름 등을 하드 코딩해야되서, 유지보수 및 변경을 하기 어렵다.
  • 가시적으로 보이지 않기 때문에 종속성 문제가 발생하기 쉽다는 단점이 있다.
더보기

에셋 번들에서 발생하는 종속성 문제는?

→ 데이터의 중복성, 같은 에셋을 중복하여 저장


어드레서블

에셋 번들의 단점을 보완하기 위해, 에셋 번들 위에서 돌아가는 어드레서블이 나오게 되었다.

 

어드레서블의 특징

  • 각 에셋에 고유한 주소(어드레스)를 부여하여 사용된다.
  • 경로 지정으로 참조할 필요가 없이 어드레스로 참조하기 때문에 하드코딩도 할 필요가 없다.
  • 에셋별로 동적 로드를 할 수 있기에 더 효율적으로 메모리 관리를 할 수 있다.
  • 에셋을 논리적인 그룹(Group)으로 묶어서 처리할 수 있다.
    └ 그룹은 유니티나 웹 서버에서 다운로드 되는 구성의 단위로 사용된다.
      다운로드가 이루어지지 않는다면, 메모리에 올라가지 않아 최적화할 수 있다.
      그룹(Group)으로 묶인 에셋들이 가시적으로 보이기 때문에 종속성 문제가 발생할 확률이 줄어든다.
  • 에셋에 라벨을 부여하여, 에셋을 묶거나 필터링할 수 있다.
      특정 라벨을 가진 에셋들을 로드할 수 있다.
  • 에셋을 원격 서버로부터 제공받을 수 있다.
  • ★ 에셋 번들에 비해 상대적으로 쉽다!! ★

어드레서블 사용 방법

1. 유니티 PackageManger에서 Addressales 패키지를 설치한다.

 

2-1. Addressables Group에서 에셋에 어드레스(Address, 주소) 부여하기

1) Window > Asset Management > Groups

 

2) Create Addressables Settings 버튼을 눌러 Addressables 그룹을 활성화 해준다.

 

3) Addressables Group에 에셋을 드래그&드랍하여 에셋에 어드레스를 부여해준다.

 

2-2. 에셋에서 직접 어드레스(Address, 주소)를 부여하기

  • Addressables 패키지를 설치하면 Addressable 토글이 생긴다.
    토글을 활성화시키면 에셋에 어드레스를 부여할 수 있다.

 

3. 에셋을 그룹화하기

  • 에셋 타입이나 특정 레벨을 로드할 때 필요한 에셋 단위로 나누고는 한다.
  • 기본적으로 추가되어있는 Default Local Group은 빌드 시에 번들로 패키징되어 빌드된 앱에 포함된다.
    다른 그룹들은 설정에 따라 빌드 시 포함이 될지 안될지 결정할 수 있다.

 

1) Create New Group > Packed Assets 로 그룹 생성하기

 

2) 에셋을 드래그&드랍하여 그룹을 나누어준다.

 

4. 에셋에 라벨을 부여하기

  • Label을 나누어주면 동일한 Label의 에셋들을 한 번에 로드 할 수 있다.

 

1) 라벨 생성하기

 

2) 라벨 부여하기

 

5. 에셋 로드하기

Addressable를 사용하여 에셋을 로드하기 위해서는 

using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

다음의 using문을 작성해주어야 한다.

 

에셋을 로드하는 방식에는 3가지가 있다.

  1. AssetReference를 이용한 에셋 로드
    public class TistoryAddressable : MonoBehaviour
    {
        public AssetReference assetReference;
        public AssetReferenceSprite spriteReference;
    
        public Transform canvas;
    
        void Start()
        {
            // 게임 오브젝트 로드
            ReferenceLoad<GameObject>(assetReference, obj =>
            {
                GameObject go = Instantiate(obj, canvas);
                Image image = go.GetComponent<Image>();
    
                // 스프라이트 로드
                ReferenceLoad<Sprite>(spriteReference, sprite =>
                {
                    image.sprite = sprite;
                });
            });
        }
    
        private void ReferenceLoad<T>(AssetReference assetReference, Action<T> action)
        {
            // assetReference.LoadAssetAsync<T>() 메소드로도 로드 가능
            AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(assetReference);
            handle.Completed += (handle) =>
            {
                action?.Invoke(handle.Result);
            };
        }
    }​


  2. 어드레스를 이용한 에셋 로드
    public class TistoryAddressable : MonoBehaviour
    {
        public Transform canvas;
    
        void Start()
        {
            // 게임 오브젝트 로드
            AddressLoad<GameObject>("FaceSprite.prefab", obj =>
            {
                GameObject go = Instantiate(obj, canvas);
                Image image = go.GetComponent<Image>();
    
                // 스프라이트 로드
                AddressLoad<Sprite>("우왁굳", sprite =>
                {
                    image.sprite = sprite;
                });
            });
        }
    
        private void AddressLoad<T>(string assetAddress, Action<T> action)
        {
            // assetReference.LoadAssetAsync<T>() 메소드로도 로드 가능
            AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(assetAddress);
            handle.Completed += (handle) =>
            {
                action?.Invoke(handle.Result);
            };
        }
    }


  3. 라벨을 이용한 에셋 로드
    public class TistoryAddressable : MonoBehaviour
    {
        public Transform canvas;
    
        void Start()
        {
            // 게임 오브젝트 로드
            AddressLoad<GameObject>("FaceSprite.prefab", obj =>
            {
                // 스프라이트 로드
                LabelLoad<Sprite>("고정멤버", sprites =>
                {
                    foreach (var sprite in sprites)
                    {
                        GameObject go = Instantiate(obj, canvas);
                        Image image = go.GetComponent<Image>();
    
                        image.sprite = sprite;
                    }
                });
            });
        }
    
        private void LabelLoad<T>(string labelName, Action<List<T>> action)
        {
            // LoadAssetsAsync => Assets에 s가 들어감
            AsyncOperationHandle<IList<T>> handle = Addressables.LoadAssetsAsync<T>(labelName, null);
            handle.Completed += handle =>
            {
                action?.Invoke(new List<T>(handle.Result));
            };
        }
    
        private void AddressLoad<T>(string assetAddress, Action<T> action)
        {
            // assetReference.LoadAssetAsync<T>() 메소드로도 로드 가능
            AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(assetAddress);
            handle.Completed += (handle) =>
            {
                action?.Invoke(handle.Result);
            };
        }
    }

6. 에셋 언로드 하기

당연하게도, 메모리에 에셋을 올려준 후에는 다시 내려야한다.

private void ReferenceUnLoad(AsyncOperationHandle handle)
{
    // IsValud 메소드: 유효성 검사
    if (handle.IsValid())
    {
        // 언로드
        Addressables.Release(handle);
    }
}

 

번외. 에셋 즉시 생성하기

위에서 게임 오브젝트를 생성할 때, 로드 따로 생성 따로 진행했었다.

Addressables.InstantiateAsync 메소드를 로드와 동시에 생성된다.

언로드는 Addressables.ReleaseInstance() 메서드로 할 수 있다.

public class TistoryAddressable : MonoBehaviour
{
    public AssetReference assetReference;
    public AssetReferenceSprite spriteReference;

    public Transform canvas;

    void Start()
    {
        Addressables.InstantiateAsync(assetReference, canvas).Completed += obj =>
        {
            Image image = obj.Result.GetComponent<Image>();

            // 스프라이트 로드
            ReferenceLoad<Sprite>(spriteReference, sprite =>
            {
                image.sprite = sprite;
            });
        };
    }

    private void ReferenceLoad<T>(AssetReference assetReference, Action<T> action)
    {
        // assetReference.LoadAssetAsync<T>() 메소드로도 로드 가능
        AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(assetReference);
        handle.Completed += (handle) =>
        {
            action?.Invoke(handle.Result);
        };
        
        // 언로드
        Addressables.ReleaseInstance(handle);
    }
}

웹 서버를 사용하여 번들 다운받기

웹 서버를 사용하여 번들 다운로드하는 과정은 해야할 게 너무 많기에,

설명 대신 내가 참고한 유튜브와 간단한 코드를 남겨보았다.

유튜브 - AWS S3 서버 사용하여 번들 적용하기

 

코드

public class TistoryAddressable : MonoBehaviour
{
    public string bundleAddress;

    void Start()
    {
        StartCoroutine(DownloadBundle());
    }

    IEnumerator DownloadBundle()
    {
        // 번들 다운로드 시작
        AsyncOperationHandle handle = Addressables.DownloadDependenciesAsync(bundleAddress, true);

        // 번들 다운로드가 완료될 때 까지 반복
        while (!handle.IsDone)
        {
            // 진행도 상태를 표시해주기
            float progress = handle.PercentComplete * 100f;
            Debug.Log($"다운로드 진행 중: {progress.ToString("F2")} %");
            yield return null;
        }

        if (handle.Status == AsyncOperationStatus.Succeeded)
        {
            Debug.Log("번들 다운로드 완료");
        }
        else
        {
            Debug.LogError("번들 다운로드 실패: " + handle.DebugName);
        }
    }
}

 


어드레서블 설정

Play Mode Script

Use Asset Database  에셋 번들을 생성하지 않고 에셋들을 직접 읽어서 빠르게 실행하는 모드
 → 그룹을 나누지 않고 통으로 관리하여 주소화된 에셋을 쉽게 관리하고 싶을 때 사용
 → 그룹 기능 사용 불가
Simulate Groups  에셋 번들을 생성하지 않지만 에셋 번들을 만든 것처럼 작동하는 모드
 → 특정 그룹의 동작을 테스트하거나, 에디터에서 그룹을 조작할 때 사용
 → 그룹 기능 사용 가능
Use Existing Build  실제 에셋 번들을 만드는 모드
 → Addressable Build를 해야 사용 가능
 → 빌드 시간을 적약하여 빠르게 테스트하고자 할 때 사용

 

Build

New Build  Addressable에서 기본적으로 제공해주는 빌드 기능
→ 번들을 생성함
Update a Previous Build  이미 빌드를 하였을 경우, 빌드 상태의 번들을 업데이트 해주는 기능
Crean Build  빌드 되어있는 번들을 지우는 기능
 All : 전부 지우기
 Content Builders : Play Mode Script 별로 빌드되어있는 상태를 지우기
 Build Pipline Cache : 빌드 후, 파이프라인에 남아있는 캐시 지우기

 

Profile

Manage Profiles  Addressables Profiles 창을 열기

기본 변수(Variable)
Local.BuildPath  빌드된 Addressable 데이터가 저장될 로컬 디렉토리의 경로
 → 빌드 시에 생성된 번들과 관련된 데이터가 저장
Local.LoadPath  로드된 Addressable 데이터가 저장될 로컬 디렉토리의 경로
 → 런타임 중에 로드된 데이터가 저장
Remote.BuildPath  빌드된 Addressable 데이터가 저장될 원격 서버 디렉토리의 경로
 → 빌드 시에 생성된 번들과 관련된 데이터가 저장
Remote.LoadPath  로드된 Addressable 데이터가 저장될 원격 서버 디렉토리의 경로
 → 런타임 중에 로드된 데이터가 저장
BuildTarget  프로젝트가 어떤 플랫폼으로 빌드되는지를 정의

 → BuildTarget.StandaloneWindows
      Windows 플랫폼을 나타냅니다.
 →BuildTarget.StandaloneLinux
      Linux 플랫폼을 나타냅니다.
 → BuildTarget.StandaloneOSX
      macOS 플랫폼을 나타냅니다.
 → BuildTarget.Android
      Android 플랫폼을 나타냅니다.
 → BuildTarget.iOS
      iOS 플랫폼을 나타냅니다.
 → [UnityEditor.EditorUserBuildSettings.activeBuildTarget]
      현재 에디터에서 사용 중인 빌드 타겟
Bundle Locations
빌드된 번들의 저장 위치를 지정
Built-In  번들이 빌드된 후, 빌드된 플랫폼에 맞게 내장되어 애플리케이션 패키지에 포함된다.
 → 번들을 애플리케이션 자체에 포함시켜야 하는 경우 사용
Editor-Hosted  에디터에서만 번들이 호스팅되며, 런타임에서는 사용되지 않는다.
 → 에디터에서 빠른 피드백 및 테스트를 위해 사용
Cloud Content Delivery  번들을 원격 서버에 업로드하고, 런타임 시 원격 서버에서 다운로드하여 사용된다.
 → 번들이 필요할 때, 원격 서버에서 다운받아 사용하고자 할 때 사용
Custom  사용자가 지정한 경로에 번들을 저장한다.
Create
Profile  Addressable Group에 대한 새로운 프로필을 만든다.
 → 각 프로파일은 해당 그룹에 대한 로딩, 다운로드, 캐싱 정책 등을 세부적으로 지정 가능
 → 플랫폼 별로 나누거나 그룹마다 설정을 다르게 하고 싶을 때 사용
 → Profile은 런타임 중에도 변경이 가능하기 때문에, 동적으로 설정을 변경할 수 있다.
Variable  모든 프로필에 새로운 변수를 만든다. 
 → 주소화된 에셋의 경로나 설정 값 등을 동적으로 변경할 때 사용
Build And Load
Path Variables
 모든 프로필에 새롭게 번들이 저장될 경로 변수를 만든다.

 

Tools

Inspect System Settings  Addressable Asset System의 ScriptableObject 설정을 볼 수 있다.
Check for Content
Update Restrictions
 컨텐츠 업데이트에 대한 제한 사항을 확인하는데 사용된다.
 → Addressable Asset System의 업데이트에 관련된 정책을 검사하고 변경할 때 사용
Window  Addressable Asset System의 Profiles, Labels, Analyze, Hosting Services, Event Viewer 창을 열 수 있다.
Groups View  Addressables Groups 창에서 그룹별로 주소화된 에셋을 어떻게 보여줄 것인가를 결정한다.

 → Show Sprite and Subobject Address
      에셋 그룹의 뷰에서 스프라이트와 서브 오브젝트의 Address를 모두 표시한다.
      비활성화 시, 스프라이트의 Address만 표시한다.
     


 → Group Hierarchy with Dashes
      그룹의 계층 구조를 대시(-)로 보여줄 것인가를 결정한다.

 

Addressable Asset Settings

 

어드레서블 에셋 설정 레퍼런스 | Addressables | 1.21.17

어드레서블 에셋 설정 레퍼런스 프로젝트에서 어드레서블 에셋의 작동 방식을 관리하려면 Addressable Asset Settings 인스펙터를 사용하십시오. 인스펙터를 열려면 Window > Asset Management > Addressables > Set

docs.unity3d.com

 

Addressable Asset Group

 

콘텐츠 패킹 및 로딩 스키마 레퍼런스 | Addressables | 1.21.17

콘텐츠 패킹 및 로딩 스키마 레퍼런스 콘텐츠 패킹 및 로딩 스키마는 기본 빌드 스크립트에서 사용되는 기본 어드레서블 스키마로, 어드레서블 에셋의 빌드 및 로드에 대한 설정을 정의합니다.

docs.unity3d.com