유니티 엔진/유니티 엔진의 구성

유니티 엔진의 구성 - 41) Addressables

monstro 2026. 5. 12. 20:22
728x90
반응형

- 개요

Addressables의 주요 기능에셋주소를 할당하고 사용하여 런타임에 로드하는 것이다

위와 같은 기능을 사용하여 모든 애셋을 한번에 로드하는 것이 아닌

필요한 시점에만 로드하여 초기 로딩 시간RAM 점유율을 대폭 감소시킬 수 있다

 

1) 문제 상황의 재연

 

위와 같이 코드를 구성하여 T 키를 누르면 prefab을 기반으로 GameObject를 생성한다

이후 프로젝트를 빌드한다 이때, 프리팹 애셋은 에디터에서 할당하여 스크립트에서 참조하므로 빌드에 포함된다

 

오브젝트를 생성하기 이전과 이후의 RAM 점유율

 

 

오브젝트가 생성된 후에 위와 같이 RAM 점유율이 급격하게 증가한 것을 확인할 수 있다

위와 같은 문제는 애셋에 대한 강한 참조로 인해 발생하는데,

로드된 씬의 스크립트가 참조하는 모든 프리팹과 그 프리팹과 연관된 모든 파일이 한꺼번에 RAM에 로드되어 발생한다

 

2) Addressable의 사용 - 강한 참조 문제의 해결

 

Addressables 애셋을 사용하기 위해서는 Addressables 패키지를 설치해야 한다

 

 

패키지 설치 이후에 상단 Window - Asset Management - Addressables - Groups 탭을 눌러

Addressables에 관련된 세팅을 진행할 수 있다

 

애셋을 Addressable 그룹에 포함시키는 방법 (1)

 

그룹을 생성하면 필수적인 파일들을 설치한 후에 기본 Addressable 그룹을 제공한다

애셋을 해당 그룹에 드래그 앤 드롭하여 그룹에 포함시킬 수 있다

 

애셋을 Addressable 그룹에 포함시키는 방법 (2)

 

또는 애셋을 선택하고 상단의 Addressable 속성을 체크하여 그룹에 포함시킬 수 있디

 

폴더를 Addressable 그룹에 포함

 

폴더를 Addressable 그룹에 포함시키는 것도 가능하다

폴더가 그룹에 들어가게 되면 폴더에 저장된 애셋들도 Addressable 그룹에 포함된다

 

3) 스크립트를 통한 Addressable 사용

3 - 1) 경로 문자열의 이용

public class Test : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            AsyncOperationHandle<GameObject> operationHandle = Addressables.LoadAssetAsync<GameObject>("Assets/GameObject.prefab");
            operationHandle.Completed += AsyncOperationHandle_Completed;
        }
    }

    private void AsyncOperationHandle_Completed(AsyncOperationHandle<GameObject> asyncOperationHandle)
    {
        if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(asyncOperationHandle.Result);
        }
        else
        {
            Debug.Log("Failed to Load");
        }
    }
}

 

위와 같이 애셋이 저장된 경로의 문자열을 이용하여 Addressable 애셋을 로드하였다

이때 LoadAssetAsync 함수를 사용하여 비동기적으로 애셋을 로드하였고

애셋의 로드가 완료된 후에 콜백함수를 호출한다

콜백함수에서로드 결과에 따라 GameObject를 생성하거나 디버그 메세지를 출력한다

 

3 - 2) 3 - 1)의 코드의 람다화

public class Test : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
                Addressables.LoadAssetAsync<GameObject>("Assets/GameObject.prefab").Completed +=
                    (operationHandle) =>
                    {
                        if (operationHandle.Status == AsyncOperationStatus.Succeeded)
                        {
                            Instantiate(operationHandle.Result);
                        }
                        else
                        {
                            Debug.Log("Failed to Load");
                        }
                    };
        }
    }
}

 

3 - 1)에서 작성한 코드에서 함수를 람다로 생성하여 불필요한 함수 정의를 생략할 수 있다

 

3 - 3) 특정 타입 애셋의 참조

public class Test : MonoBehaviour
{
    [SerializeField]
    private AssetReference assetReference;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            assetReference.LoadAssetAsync<GameObject>().Completed +=
                (operationHandle) =>
                {
                    if (operationHandle.Status == AsyncOperationStatus.Succeeded)
                    {
                        Instantiate(operationHandle.Result);
                    }
                    else
                    {
                        Debug.Log("Failed to Load");
                    }
                };
        }
    }
}

 

AssetReference 클래스를 사용하면 로드한 애셋특정 타입으로 사용할 수 있다

위와 같이 에디터에서 assetReference 변수의 값을 할당받고

LoadAssetAsync 함수의 제네릭 인자를 GameObject로 넘겨주어 로드한 애셋을 GameObject로 사용한다

 

3 - 4) 라벨의 사용

Addressable 그룹의 라벨 사용

 

라벨을 사용하면 여러 애셋에 동일한 라벨을 붙여 한 번에 묶음으로 로드할 수 있다

 

public class Test : MonoBehaviour
{
    [SerializeField]
    private AssetLabelReference assetLabelReference;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            Addressables.LoadAssetsAsync<GameObject>(assetLabelReference, (gameObject) => {
                if (gameObject)
                {
                    Instantiate(gameObject);
                }
                else
                {
                    Debug.Log("Failed to create " + gameObject.name);
                }
            });
        }
    }
}

 

AssetLabelReference 클래스를 사용하여 생성한 라벨이 붙여진 애셋들을 한번에 로드할 수 있다

LoadAssetsAsync 함수에 인자로 에디터에서 설정한 AssetLabelReference를 넘겨주어 사용한다

라벨이 붙여진 애셋을 문제없이 로드했다면 생성하고, 아니라면 애셋의 이름을 출력한다

 

3 - 5) 커스텀 타입의 참조

[System.Serializable]
public class AssetReferenceCharacter : AssetReferenceT<GameObject> {
    public AssetReferenceCharacter(string guid) : base(guid) { }
}

public class Test : MonoBehaviour
{
    [SerializeField]
    private AssetLabelReference assetLabelReference;
    [SerializeField]
    private AssetReferenceCharacter characterAssetReference;

	...
}

 

AssetReferenceT<타입> 클래스를 상속받아 로드한 애셋을 원하는 커스텀 타입으로 사용할 수 있다

위의 예시에서는 characterAssetReference 변수가 GameObject 타입 애셋만 할당가능하도록 코드를 구성하였다

 

3 - 6) 로드와 생성의 동시 처리

public class Test : MonoBehaviour
{
    [SerializeField]
    private AssetReferenceGameObject assetReferenceGameObject;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            assetReferenceGameObject.InstantiateAsync();
        }
    }
}

 

InstantiateAsync 함수를 사용하면 애셋을 로드함과 동시에 생성할 수 있다

예시에서는 assetReferenceGameObject 변수로부터 함수를 호출하여 GameObject 애셋을 로드하고 생성한다

 

3 - 7) 로드된 인스턴스의 해제

public class Test : MonoBehaviour
{
    [SerializeField]
    private AssetReferenceGameObject assetReferenceGameObject;

    private GameObject spawnedGameObject;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            assetReferenceGameObject.InstantiateAsync().Completed += (asyncOperation) => spawnedGameObject = asyncOperation.Result;
        }

        if (Input.GetKeyDown(KeyCode.U))
        {
            assetReferenceGameObject.ReleaseInstance(spawnedGameObject);
        }
    }
}

 

 

Addressable 애셋을 로드하면 내부적으로 참조 카운트가 증가하는데, 이 카운트가 0이 되어야 메모리에서 제거된다

따라서 애셋을 로드한 후에 해제하지 않는다메모리 누수가 발생하게 된다

ReleaseInstance 함수를 호출하여 로드된 애셋을 해제할 수 있다

따라서 반드시 로드 - 해제의 과정은 을 이뤄 수행해야 한다

 

4) Addressable 기능

 

Addressable에서 추가적으로 사용할 수 있는 기능은 다음과 같다

  • Profiles 탭 : 개발 환경에 따라 애셋의 로드 경로를 다르게 설정할 수 있음
  • Tools - System 탭 : Addressable에 관련된 설정을 조정가능

 

- 최종 실행 결과

Addressable 애셋을 사용하여 오브젝트를 생성하기 이전과 이후의 RAM 점유율

 

최종적으로 Addressable 애셋을 사용하는 경우 위와 같이 RAM 사용량을 감소시킬 수 있다

728x90
반응형