본문 바로가기

Unity/Coding

Addressables in Unity

유니티에는 에셋을 다루는 다양한 방법이 있다.

몇 가지 예를 들자면,

  • 변수를 Inspector에 노출시키고 Hierarchy에서 직접 에셋을 드래그&드롭으로 연결시켜놓는 방법.
  • Resources 폴더를 에셋을 넣고 Resources.Load()를 사용해 불러오는 방법.
  • 에셋들을 묶음(Bundle) 단위로 관리, 배포하는 방법.

어드레서블은 다른 방식들 몇가지 단점들을 보완하고자 만들어진 방식이다.


어드레서블이란?

  • 어드레서블은 유니티에서 사용되는 에셋 관리 시스템이다.
  • 원하는 타이밍에 에셋을 로드할 수 있기 때문에 메모리 관리에 도움이 된다.
  • 모든 에셋을 Resources.Load로 불러온다면 미리 메모리에 올려두어야 하고, 이는 로딩 타임에도 영향을 끼쳐 앱의 경쟁력이 떨어질 수 있으니, 프로젝트의 규모가 어느 정도 있다면 어드레서블 사용을 고려해 보자.
  • 어드레서블은 비동기로 실행되기 때문에 멈춰진 화면을 보고만 있는 일은 없을 것이다.
  • 백그라운드에서 로드가 끝났을 때 원하는 처리 하도록 만들자.
  • 어드레서블을 클라우드에 있는 서버에 올려서 사용할 수도 있다. 이런 경우 빌드를 다시 받지 않아도 새로운 에셋들을 배포할 수 있게 된다.

 

로컬에서 Addressable 사용하기

  1. Package Manager에서 Addressables를 인스톨하자.
  2. Window > Asset Management > Addressables > Groups를 선택해 Addressables Groups 팝업을 띄운다.
  3. 그룹을 설정하고 원하는 에셋을 해당 그룹으로 그레그하면 등록시킬 수 있다. (에셋을 어드레서블로 등록시키는 방법은 다양하다)

 

마우스를 클릭하면 어드레서블 프리팹을 생성하는 간단한 스크립트이다.

지금은 경로를 string으로 전달하는 끔찍한 방식을 사용하고 있지만 다른 방식도 아래 설명될 것이다.

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

public class TestAddressables : MonoBehaviour
    {
        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                var asyncOperationHandler = 
                    Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Characters/Players/Player.prefab");
                asyncOperationHandler.Completed += HandleOperation;
            }
        }

        private void HandleOperation(AsyncOperationHandle<GameObject> operation)
        {
            if (operation.Status == AsyncOperationStatus.Succeeded)
            {
                Instantiate(operation.Result);
            }
            else
            {
                Debug.Log("Failed to load!");
            }
        }
    }

 

람다식을 확용하여 좀 더 간결한 표현이 가능하다.

public class TestAddressables : MonoBehaviour
    {
        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Characters/Players/Player.prefab").Completed +=
                    (operation) =>
                    {
                        if (operation.Status == AsyncOperationStatus.Succeeded)
                        {
                            Instantiate(operation.Result);
                        }
                        else
                        {
                            Debug.Log("Failed to load!");
                        }
                    };
            }
        }
    }

자, 이제 끔찍한 string 대신에 다른 Addressable 활용법들을 알아보자

먼저 Asset Reference를 살펴보자.

다음과 같이 변수를 선언하면 인스펙터에서 원하는 에셋을 드레그 해서 등록할 수 있다.

[SerializeField] private AssetReference assetReference;

 

 

Asset Reference를 통해 프리팹을 생성해 보자.

public class TestAddressables : MonoBehaviour
    {
        [SerializeField] private AssetReference assetReference;
        
        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                assetReference.LoadAssetAsync<GameObject>().Completed +=
                    (operation) =>
                    {
                        if (operation.Status == AsyncOperationStatus.Succeeded)
                        {
                            Instantiate(operation.Result);
                        }
                        else
                        {
                            Debug.Log("Failed to load!");
                        }
                    };
            }
        }
    }

string이 없어지니 한결 후련하다!

다음과 같이 간단하게 Addressable GameObject를 생성 방법도 있다.

assetReference.InstantiateAsync();

 


어드레서블을 로드하는 다른 방법도 알아보자.

Lable을 통해 어드레서블 에셋 로드하기.

원하는 Lable을 추가해서 다수의 에셋을 해당 Lable로 등록 시킬 수 있다.

[SerializeField] private AssetLabelReference assetLabelReference;

 

인스펙터에 노출해서 원하는 레이블을 선택할 수 있다.

private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Addressables.LoadAssetAsync<GameObject>(assetLabelReference).Completed +=
                (operation) =>
                {
                    if (operation.Status == AsyncOperationStatus.Succeeded)
                    {
                        Instantiate(operation.Result);
                    }
                    else
                    {
                        Debug.Log("Failed to load!");
                    }
                };
        }
    }

폴더 전체를 Addressable로 했을 때, 다수의 에셋들을 로딩하는 방법도 살펴보자.

 

폴더를 Addressable로 등록을 했더라도 폴더를 다이렉트로 접근하는 건 불가능하다.

하위에 있는 에셋에 일일히 접근해야 하는데 이런 접근 방법은 우리가 원하는 방법이 아니기 때문에

Lable을 사용하는게 바람직하다.

 

폴더를 Lable로 지정하면 하위에 에셋들이 모두 해당 Lable로 마크된다.

아까 언급한 AssetLableReference로 해당 폴더를 등록한다.

[SerializeField] private AssetLabelReference assetLabelReference;

private void Update()
{
    if (Input.GetMouseButtonDown(0))
    {
        Addressables.LoadAssetsAsync<GameObject>(assetLabelReference, (go) => 
        { 
        	Debug.Log(go);
        });
    }
}

두 번째 인자에 에셋을 불러올 때마다 실행하는 Action을 넣어줄 수 있다.


더 이상 불러온 에셋을 사용하지 않는다면 Release 해주도록 하자.

이 방법으로 메모리 공간을 절약할 수 있다.

[SerializeField] private AssetReferenceGameObject assetReferenceGameObject;

private GameObject spawnedGameObject;

private void Update()
{
    //불러오기
    if (Input.GetMouseButtonDown(0))
    {
        assetReferenceGameObject.InstantiateAsync().Completed +=
            (operation) => spawnedGameObject = operation.Result;
    }

    //놓아주기
    if (Input.GetMouseButtonDown(1))
    {
        assetReferenceGameObject.ReleaseInstance(spawnedGameObject);
    }
}

레퍼런스는 GameObject나 Sprite 등 강한 타입들도 지원을 하니 시간이 된다면 살펴보자.

[SerializeField] private AssetReferenceSprite assetReferenceSprite;
[SerializeField] private AssetReferenceGameObject assetReference;

 

아래 코드와 같이 원하는 타입을 만들어서 사용할 수도 있다.

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

 

 

'Unity > Coding' 카테고리의 다른 글

JSON in Unity  (1) 2022.12.30
PlayerPrefs  (0) 2022.12.28