리소스 폴더 사용
장점은 사용하기에 편리하지만, 리소스폴더에 메모리는 최대한 줄이도록 한다. 시작 시 최소한의 에셋만 남겨두어야겠지만 경계해야 한다.
단점)
1. apk사이즈가 커진다.
2. 앱 시작 시간이 길어진다.
3. 앱이나 폴더 변경 시 재 빌드를 해야 한다. (apk빌드에 묶이기 때문에 에셋 변경 시 무조건 재 빌드해야 한다.)
4. 에셋 이름 변경이 힘들다. (에셋을 로드할 때 경로를 바꿔줘야 하기 때문)
이런 문제점들을 보완할 수 있는 에셋 번들이 있다.
에셋 번들
장점은 에셋을 묶음 단위로 관리할 수 있다. 빌드 사이즈 절감과, 앱 시작 시간을 단축시킨다.
단점으로는
1. 번들의 종속성 문제.
같은 이미지를 Asset A와 Asset B에 묶여있다면 같은 이미지 1개로 처리될 것 같지만 2개의 이미지로 인식된다.
이해하기가 다소 어려우며 하드코딩 또한 불가피하다.
예제
using UnityEngine;
using UnityEngine.UI;
public class AssetBundleRef: MonoBehaviour
{
[SerializeField] private string _bundleName;
[SerializeField] private Image _img0;
[SerializeField] private Image _img1;
private GameObject _go;
private AssetBundle _assetBundle;
// 로컬 저장소로부터 에셋 로드
private void OnEnable()
{
_assetBundle =
// 에디터 path = "[프로젝트 디렉토리]\Assets\StreamingAssets/test"
// 런타임 path = "[앱 설치 디렉토리]\[프로젝트명]_Data\StreamingAssets/test"
AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + _bundleName);
_img0.sprite = _assetBundle.LoadAsset<Sprite>("Apple");
_img1.sprite = _assetBundle.LoadAsset<Sprite>("Banana");
var prefab = _assetBundle.LoadAsset<GameObject>("SomePrefab");
_go = Instantiate(prefab, transform);
}
// 에셋 언로드
private void OnDisable()
{
_assetBundle.Unload(true);
}
}
저장소로부터 에셋번들과 에셋을 각각 '경로'와 '파일명'의 string으로 매칭시킨다.
이는 변경에 취약하다. 문자열의 오타로 인해 매칭이 끊어져버릴 수 있기 때문에 보완하기 위해 구조 개선 프로그래밍이 필요한데 개발자가 사용을 꺼리는 이유다.
이러한 에셋 번들(빌드와 번들의 분리)의 장점과 리소스 폴더의 장점(비교적 편리함)을 가진 어드레 서블 에셋 시스템이다.
어드레 서블 에셋
어드레서블 에셋이란 어드레스가 할당된 에셋이다. 어드레스를 이용하여 에셋 위치에 상관없이 참조가 가능하다.
어드레스를 이용하여 에셋의 관리, 로딩, 빌드가 통합된 시스템이다. 어드레서블은 에셋번들을 토대로 설계된 시스템이다. 완전히 새롭게 만들어진 것이 아니라 에셋번들의 편의성을 개선하기 위해 등장한 시스템이다.
장점으론
- 에셋 빌드와 배포의 단순화 : 직접 레퍼런스, 리소스 폴더, 에셋 번들 분리가 단순화되고 편리해진다.
- 효율적인 에셋 관리 : 종속성 파악 및 메모리 로드/언로드 현황을 볼 수 있다. (중복된 에셋으로 메모리를 낭비가 되는 것을 막는다.)
- 초기 빌드 볼륨을 최소화할 수 있다.
- 에셋별 동적 로드를 통해 메모리 관리가 가능하다.
- 에셋을 Remote 서버(CDN; Contents Delivery Network)로부터 제공할 수 있다.
- 빌트인 에셋으로부터 필요한 에셋을 업데이트하는 증분(Incremental) 업데이트가 가능하다. "DLC"
vs 에셋번들
에셋번들과 어드레서블의 가장 큰 차이는 저장소로부터 에셋을 가져오는 방식이다.
어드레서블은 에셋에 Address를 부여하는데, 이 Address로 에셋들을 참조한다.
에셋 번들은 중개자 없이 경로를 통해 가져오는 방식인데, 어드레서블은 address라는 중개자를 통해 가져온다. 모든 번거로운 일은 address가 대신 해준다.
어드레서블 에셋 시스템 주로 쓰이는 곳
1. 대규모 프로젝트에서 메모리를 효율적으로 관리하고 싶을 때 (참고: 어드레서블 에셋 시스템으로 메모리 최적화하기)
2. 서버에서 추가 에셋 다운로드가 필요할 때
어드레서블 패키지 설치
어드레서블 에셋 시스템을 사용하려면 Addressables 패키지를 설치해야 한다.
Package Manager 에서 Addressables 를 install 해준다.
설치가 끝나고 Asset을 눌러보면 이렇게 Addressable 토글 부분이 생긴다.
에셋을 어드레서블로 만들기 (어드레스 부여하기)
Addressable 토글 버튼을 활성화 하면 주소를 입력하는 부분이 나온다. 디폴트로 현재 에셋의 path가 주소로 설정이 되는 데 수정 가능하다.
활성화 하고 Window > Asset Management > Addressables > Groups 에 들어가보면
기본 로컬 그룹에 추가되어있는 것을 볼 수 있다.
그룹을 추가하고 싶다면 우클릭한다.
드래그앤드랍으로 다른 그룹으로 에셋을 이동시킬 수 있다.
이 블로그에서 가져온 그룹과 라벨에 대한 설명
나중에 빌드를 하게되면 그룹마다 하나의 번들로 묶이게 된다.
반대로 말하면 그룹 내 하나의 리소스만 사용하려 해도 해당 그룹 내 번들을 전부 다운로드 받아야만 사용이 가능하다.
Labels의 기능은 추후 Labels의 값으로 동일한 Label의 에셋들을 한번에 로드할 수 있다.
그룹은 실제 파일들을 묶는 용도라 보시면 되고 Label은 로드할때 쓰는 용도다.
에셋을 로드하기
1. Addressables.LoadAsset(s)Async // 유튭설명
2. Addressables.InstantiateAsync // 유튭설명
이 두 메소드를 사용해줄 수 있고 자세한 것을 유튜브 설명을 참고
에셋을 서버에 올리고 다운받기
Profiles에 들어가면
RemoteLoadPath 를 설정해주는 필드가 있다.
사용 방법 (코드)
Addressable 사용을 위해서는 스크립트에 다음과 같은 using을 해줘야 한다.
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
번들 다운로드 시
CDN처럼 번들을 서버에 올려놓고 받을 때 사용하는 함수는 다음과 같이 작성하여 호출해 주면 된다.
/// <summary>
/// 어드레서블 다운로드 함수.
/// </summary>
/// <param name="key"></param>
public void DownloadDependenciesAsync(object key)
{
Addressables.GetDownloadSizeAsync(key).Completed += (opSize) =>
{
if(opSize.Status == AsyncOperationStatus.Succeeded && opSize.Result > 0)
{
Addressables.DownloadDependenciesAsync(key, true).Completed += (opDownload) =>
{
if (((AsyncOperationHandle)opDownload).Status != AsyncOperationStatus.Succeeded)
return;
//다운로드 완료 처리.
};
}
else
{
//이미 다운로드가 완료된 상태 처리.
}
};
}
번들 로드 시
필요한 번들을 로드 시 아래와 같이 작성해주면 된다.
public void LoadAssetAsync(object key)
{
try
{
Addressables.LoadAssetAsync<GameObject>(key).Completed += (op) =>
{
if (((AsyncOperationHandle<GameObject>)op).Status == AsyncOperationStatus.Succeeded)
return;
//로드 완료 처리.
};
}
catch(Exception e) { Debug.LogError(e.Message); }
}
로드한 번들 생성 시
Editor에 AssetReference를 캐싱받은 후 번들을 생성하는 경우 다음과 같이 작성해주면 된다.
//Addressable 프리팹 할당
public AssetReference SpawnablePrefab;
public void CreatePrefab()
{
List<AsyncOperationHandle<GameObject>> handles = new List<AsyncOperationHandle<GameObject>>();
AsyncOperationHandle<GameObject> handle = SpawnablePrefab.InstantiateAsync();
handles.Add(handle);
}
로드한 번들 해제 시
생성된 번들을 해제(삭제) 시에는 다음 코드를 작성해주면 된다.
'Instantiate <-> ReleaseInstance' / 'LoadAsset <-> ReleaseAsset'
각 함수에 맞는 해제함수를 사용해야 한다. 그렇지 않으면 Ref Counting에 대한 이슈가 발생한다.
using UnityEngine;
using UnityEngine.AddressableAssets;
public class SelfDestruct : MonoBehaviour
{
[SerializeField] private float lifetime = 2f;
private void Start()
{
// 지연 후 함수 호출
Invoke("Release", lifetime);
}
void Release()
{
// Instantiate <-> ReleaseInstance
Addressables.ReleaseInstance(gameObject);
}
}
예제 2
1. 해당 프리 팹에 어드레스 부여 하기.
어드레 서블 체크박스에 체크하고 어드레스를 부여하면된다. 그리고 잘 되어있는지 어드레서블 현황 대시보드를 통해 확인한다.
2. 에셋 로드하기.
1) Lables 으로 로드하기.
Addressables.LoadAssetsAsync(type.ToString(), (Object obj) =>
{
if (!m_dicObject.ContainsKey(type))
m_dicObject.Add(type, new Dictionary<string, Object>());
m_dicObject[type].Add(obj.name, obj);
});
위 코드에 type이 어드레서블에 Lables이다.
2) 레퍼런스로 로드하기.
이런 식으로 인스펙터에 레퍼런스를 지정하여 로드하는 방식이다.
3) 어드레 서블 이름으로 로드하기.
public void LoadAsset(string strAddressableName)
{
Addressables.InstantiateAsync(strAddressableName);
}
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;
public class AddressablesManager : MonoBehaviour
{
[SerializeField] private Image image;
[SerializeField] private AssetReference assetReference;
[SerializeField] private AssetReferenceGameObject assetReferenceGameObject;
[SerializeField] private AssetReferenceSprite AssetReferenceSprite;
public void AddressablesPrefab()
{
// 메모리 로드 구문 없이 바로 Instantiate
Addressables.InstantiateAsync(assetReferenceGameObject);
}
public void AddressablesScene()
{
// 메모리 로드 구문 없이 바로 씬 로드
assetReference.LoadSceneAsync(UnityEngine.SceneManagement.LoadSceneMode.Additive);
}
public void AddressablesSprite()
{
// 메모리에 로드 후 콜백 호출
AssetReferenceSprite.LoadAssetAsync().Completed += OnSpriteLoaded;
}
// 콜백 함수
private void OnSpriteLoaded(AsyncOperationHandle<Sprite> handle)
{
image.sprite = handle.Result;
}
}
어드레서블은 AddressableAssets.AssetReference와 같은 클래스를 통해 직접 참조한다.
매칭 과정이 없기 때문에 변경 시 따르는 코드 수정 작업도 필요 없다.
어드레서블 창에서 모든 에셋 그룹을 확인할 수 있고 편집할 수 있어서 에셋 관리가 용이하다.
종속성(의존성, Dependancy) 관리도 자동으로 해주기 때문에 동일 에셋의 중복 번들링을 피할 수 있다. 즉, 에셋이 속할 수 있는 에셋 그룹은 단 하나로 유일하다.
'게임엔진 > Unity' 카테고리의 다른 글
[Unity] JSON 파일 저장 및 읽기 (0) | 2023.05.11 |
---|---|
[Unity] 이동, 회전, 크기변경 정리 (2) | 2023.05.05 |
[Unity] Job 시스템 정의 (0) | 2023.03.23 |
[Unity] Job 시스템 이해 4, IJobParallelFor (0) | 2023.03.23 |
[Unity] Job 시스템 이해 3, JobHandle (0) | 2023.03.23 |