티스토리 뷰
## Addressables system(주소지정가능 시스템)
- 런타임에 자산을 load 및 release 하기위한 API와 content를 구성 및 패키징하는 tools 및 scripts 제공
- 주소로 Asset을 로드하는 쉬운 방법 제공 (기존 직접참조, Resources폴더, AssetBundle의 상위호환 추상화 계층)
- 자산 호스팅 위치를 코드수정 없이 유연하게 변경 가능(local, CDN 등)
- Dependency management : content를 반환하기 전에 종속된 모든 mesh, shader, animation 등이 로드됨
- Memory management : reference를 자동으로 계산하고 profiler를 통한 메모리 프로파일링 기능 제공
- 내부적으로 Weak reference 사용?
- 로컬 및 원격 배포용 자산을 쉽고 효율적으로 packing할 수 있도록 지원 (AssetBundles를 기반으로함)
### Package dependencies
### 주요 키워드
- Address : asset의 위치ID (location identifier) 문자열
- Label : 비슷한 아이템들의 런타임 로딩을 위해 제공되는 추가적인 Addressable Asset ID
- `AssetReference` : addressable asset에 대한 참조. 직접참조처럼 작동하지만 초기화가 지연되는 객체. 요청시 로드할 수 있는 Addressable로 GUID를 저장. 참조가 설정되었는지 `RuntimeKeyIsValid()`를 통해 확인가능.
- `IResourceLocation` : 자산 및 해당 종속성을 로드하기 위한 정보가 포함된 중간 객체
- AddressableAssetData 디렉토리 : 프로젝트의 Assets디렉토리에 저장되는 Addressable Asset metadata
- Asset group : build-time processing에 사용할 수 있는 Addressable Asset 집합 (Asset group schema로 정의됨)
## Addressable Asset 추가 방법
- 인스펙터에서 Addressable 체크박스를 체크시
- 인스펙터에서 `AssetReference`타입의 필드에 에셋 연결시
- Addressables Groups창(Window > Asset Management > Addressables > Group)에 드래그&드랍으로 추가시
- Addressable로 체크한 프로젝트 폴더에 에셋 추가시
## Addressable 자산 로드시 내부 수행순서
1. 해당 key에 대한 resource 위치 조회 (`IResourceLocation` key 제외)
2. 종속성(dependencies) 목록 수집
3. 필요한 모든 원격(remote) AssetBundles 다운로드
4. AssetBundles를 메모리에 로드
5. 작업(operation)의 Result객체를 로드된 객체로 설정
6. 작업의 Status를 업데이트하고 `Completed` 이벤트 리스너를 호출
## 주요 API
### GameObject
- `GameObject.Instantiate` : 게임오브젝트 인스턴스화
- `GameObject.Destory` : `GameObject.Instantiate`(또는 Addressables가 아닌 방식)로 생성된 객체 인스턴스 제거
### Addressables
자산로드 (LoadAssetAsync)
참조카운트 1 증가(매 호출시마다). 최초 1회 로드후 기존 `Instantiate`로 다수의 객체를 참조카운트 증가 없이 인스턴스화할때 유용. (e.g. Object pooling)
- `Addressables.LoadAssetAsync<TObject>` : 단일 에셋 로드
- `Addressables.LoadAssetsAsync<TObject>` : 여러 에셋 로드
- `releaseDependenciesOnFailure` : 로드 오류 처리 방법 지정. true면 오류 발생시 성공적으로 로드된 작업 및 자산도 모두 중단 및 해제. false면 오류발생한 자산만 null로 해서 모든 작업 완료 후 반환.
- `Addressables.Release` : 작업(operation) 및 관련 리소스 해제
- `Addressables.Release<TObject>` : 에셋 해제
자산인스턴스화 (InstantiateAsync)
참조카운트 1 증가(매 호출시마다). 내부적으로 LoadAssetAsync 사용 및 인스턴스화함. persistent scene에 올라가는 manager 인스턴스와 같은 예에서 활용가능
- `Addressables.InstantiateAsync` : 단일객체 로드 및 인스턴스화
- instantiationParameters : 다수의 인스턴스화에 동일한 인자값 사용시 해당 구조체를 통해 사용
- trackHandle : 작업핸들을 Addressables시스템에서 추적할지 여부. true시 `Addressables.ReleaseInstance`를 통해 자산 릴리즈 가능(또는 종속된 Scene 언로드시 자동해제됨). false시 작업핸들을 직접 관리 및 해제 필요.
- `Addressables.ReleaseInstance` : `Addressables.InstantiateAsync`로 생성된 객체를 해제(release)하고 제거(destroy)
Scene로드 (LoadSceneAsync)
- `Addressables.LoadSceneAsync` : Scene 로드
- `activateOnLoad` : 로드가 완료되는 즉시 Scene을 활성화할지, `SceneInstance.ActivateAsync` 호출시까지 대기할지 여부 (AsyncOperation.allowSceneActivation과 동일. 기본값은 true. false설정시 Scene이 활성화될때까지 다른 Addressable에셋 로드를 포함하여 `AsyncOperation` 대기열(queue)이 차단됨에 유의)
- `Addressables.UnloadSceneAsync` : `Addressables.LoadSceneAsync`를 통해 로드된 Scene 해제
### AssetReference
주로 Scene 등에 미리 직렬화 구성하고 런타임에 Lazy 로드가 필요한 경우 사용. `Addressables`의 LoadAssetAsync, InstantiateAsync, LoadSceneAsync 및 Release API 지원(static method와 instance의 method 차이)
- `assetReference.LoadAssetAsync<TObject>`
- `assetReference.ReleaseAsset`
- `assetReference.InstantiateAsync`
- `assetReference.ReleaseInstance`
- `assetReference.LoadSceneAsync`
## Addressables시스템의 참조 카운트 (공식문서, 포럼)
Addressables시스템은 참조카운팅을 사용하여 자산이 사용중인지 여부를 결정하기 때문에 로드(load)하거나 인스턴스화(instantiate)하는 모든 자산은 사용이 끝나면 해제(release)해야함
- `Addressables.LoadAssetAsync` : 참조카운트 1 증가 (이후 `Object.Instantiate` 사용시 카운트는 그대로)
- 로드된 자산 또는 작업핸들을 해제시 참조카운트가 0이되면 관련된 자산이 언로드되고 모든 인스턴스화된 복사본도 해당 하위 자산을 잃게됨(e.g. Scene에 GameObject는 존재하지만 의존하는 mesh나 texture는 없음)
- `Addressables.InstantiateAsync` : 호출시마다 참조카운트 1 증가. 해제시 개별적으로 `Addressables.ReleaseInstance` 호출
- Scene 언로드시 내부적으로 trackHandle되는 `InstantiateAsync`로 생성된 addressable 인스턴스들은 자동으로 해제됨
- `LoadAssetAsync`를 통해 로드된 객체(texture, material, game object, etc)는 자동해제되지 않으므로 수동으로 `Release` 호출 필요
## 여러 자산 로드시 개별 자산 로드 순서 설정
기본적으로 한 operation에서 여러 자산을 로드할때 개별 자산이 로드되는 순서는 key 목록 순서와 반드시 동일하지 않음
key 목록 순서와 개별 자산 로드 순서를 동기화하고 싶다면 `IResourceLocation`을 사용할 수 있음
## 비동기 작업(Operations) 핸들링 방법
### Coroutines and IEnumerator
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadWithIEnumerator : MonoBehaviour
{
public string address;
AsyncOperationHandle<GameObject> opHandle;
public IEnumerator Start() {
opHandle = Addressables.LoadAssetAsync<GameObject>(address);
if (!opHandle.IsDone)
yield return opHandle;
if (opHandle.Status == AsyncOperationStatus.Succeeded) {
Instantiate(opHandle.Result, transform);
} else {
Addressables.Release(opHandle);
}
}
void OnDestroy() {
Addressables.Release(opHandle);
}
}
### C# Event
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadWithEvent : MonoBehaviour
{
public string address;
AsyncOperationHandle<GameObject> opHandle;
void Start() {
opHandle = Addressables.LoadAssetAsync<GameObject>(address);
opHandle.Completed += Operation_Completed;
}
private void Operation_Completed(AsyncOperationHandle<GameObject> obj) {
if (obj.Status == AsyncOperationStatus.Succeeded) {
Instantiate(obj.Result, transform);
} else {
Addressables.Release(obj);
}
}
void OnDestroy() {
Addressables.Release(opHandle);
}
}
### C# Task (async/await)
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadWithTask : MonoBehaviour
{
// Label or address strings to load
public List<string> keys = new List<string>() { "characters", "animals" };
// Operation handle used to load and release assets
AsyncOperationHandle<IList<GameObject>> loadHandle;
public async void Start() {
loadHandle = Addressables.LoadAssetsAsync<GameObject>(
keys, // Either a single key or a List of keys
addressable => {
// Called for every loaded asset
Debug.Log(addressable.name);
}, Addressables.MergeMode.Union, // How to combine multiple labels
false); // Whether to fail if any asset fails to load
// Wait for the operation to finish in the background
await loadHandle.Task;
// Instantiate the results
float x = 0, z = 0;
foreach (var addressable in loadHandle.Result) {
if (addressable != null) {
Instantiate<GameObject>(addressable,
new Vector3(x++ * 2.0f, 0, z * 2.0f),
Quaternion.identity,
transform); // make child of this object
if (x > 9) {
x = 0;
z++;
}
}
}
}
private void OnDestroy() {
Addressables.Release(loadHandle);
// Release all the loaded assets associated with loadHandle
// Note that if you do not make loaded addressables a child of this object,
// then you will need to devise another way of releasing the handle when
// all the individual addressables are destroyed.
}
}
Task방식 사용시 `Task`클래스의 method들 사용가능
// Load the Prefabs
var prefabOpHandle = Addressables.LoadAssetsAsync<GameObject>(
keys, null, Addressables.MergeMode.Union, false);
// Load a Scene additively
var sceneOpHandle
= Addressables.LoadSceneAsync(nextScene,
UnityEngine.SceneManagement.LoadSceneMode.Additive);
await System.Threading.Tasks.Task.WhenAll(prefabOpHandle.Task, sceneOpHandle.Task);
## Scene에서의 Addressabes
- Addressable로 지정된 Scene의 내부 자산들은 암시적으로 종속성이 부여되고 content 빌드를 만들때 Scene과 동일한 AssetBundle에 자산을 압축. 단, non-Addressable이 아닌 개별적으로 Addressable로 지정된 자산들은 속한 그룹에 따라 고유한 AssetBundle에 포함됨.
- non-Addressable로 지정된 Scene 내부에 배치된 Addressable자산은 암시적 종속성이 되어 built-in 데이터에 해당 자산의 사본이 포함됨에 유의 (이 경우, AssetReference를 통해 장면에 미리 구성하고 런타임에 Lazy로드하는 방법 사용가능)
- 둘 이상의 위치에서 사용되는 암시적 종속성은 여러 AssetBundle 및 Scene 내부에 복제될 수 있으므로 Analyze툴의 Check Duplicate Bundle Dependecies rule을 통해 중복을 찾고 제거 필요
## Addressable자산 Profiling
- Event Viewer 활성화 : "Window > Asset Management > Addressables > Settings"의 Send Profiler Events 체크
- Event Viewer 사용 : "Window > Asset Management > Addressables > Event Viewer"
## Addressables시스템을 통한 Contents 업데이트
- Addressables은 코드가 아닌 content만 배포가능
- 코드 변경시에는 새로운 Player build와 Content build가 필요 (e.g. prefab의 monobehaviour컴포넌트가 참조하는 script)
- Script API 변경없이 내부로직 변경 assembly를 재배포하는 방법은 가능한지 확인필요
## 메모리 관리
- 그룹화 및 종속성 관리를 통해 적절한 시점에 관련된 자산들이 효율적으로 메모리를 사용하고, 필요한 시점에 메모리에서 내려가서 메모리 관리가 효율적으로 되도록 구성 필요
- Load시 Release 필요
- Instantiate시 ReleaseInstance필요 : 공식문서
- instantiationParameters : 다수의 인스턴스화에 동일한 인자값 사용시 해당 구조체를 통해 사용
- trackHandle : 작업핸들을 Addressables시스템에서 추적할지 여부. true시 `Addressables.ReleaseInstance`를 통해 자산 릴리즈 가능. false시 작업핸들참조를 직접 저장 및 해제
## 기타
- 로드시 `WaitForCompletion`은 실행 스레드를 차단하고 동기적으로 대기. Unity 2020.1 이하 버전에서는 상황에 따라 효율적일 수도 있으나 Unity 2020.2 이상에서는 비동기 방식과 성능차이가 미미하기에 사용 지양
- Google PAD(Play Asset Delivery)와 연동하려면 아래 링크를 참조
- Google 공식 Unity plug-in : 단, Addressables가 아닌 레거시 AssetBundles시스템을 사용함에 유의
- 처음에는 로직 script 부분까지 라이브업데이트가 가능할거라 기대했는데 해당부분 수정은 전체를 재빌드 및 배포해야한다. (포럼공식답변) 라이브 업데이트하는 다른 방법이야 많겠지만 ScriptableObject와 같이 직렬화 자산으로 로직을 구성한다면 Addressables를 통한 라이브 업데이트도 가능할것 같다. 하지만 이경우 해킹에 취약해질것 같다.
- Unity는 API들이 빠르게 변경되고 문서 업데이트가 느린 경우가 많아서 관련 문서나 영상들은 오래될수록 잘못되어 있는 경우가 많다. 항상 최신버전의 공식 문서나 공식 답변이 아닌 경우에는 의심하는것이 좋다.
- 기타 자세한 사용방법은 아래의 링크 참조
## References
https://docs.unity3d.com/Packages/com.unity.addressables@1.19/manual/index.html
https://docs.unity3d.com/Packages/com.unity.addressables@1.19/api/UnityEngine.AddressableAssets.html
https://blog.unity.com/kr/games/addressable-asset-system
https://unity.com/how-to/simplify-your-content-management-addressables
https://github.com/Unity-Technologies/Addressables-Sample
'Game > 개발' 카테고리의 다른 글
[Unity] uGUI (0) | 2023.03.25 |
---|---|
[Unity] Assembly Definition (어셈블리 정의) (0) | 2023.03.16 |
[Unity] Preprocessor directives & Conditional Compilation (전처리기 지시문 및 조건부 컴파일) (0) | 2023.03.15 |
[Unity] 엔진의 주요 event 함수 정리 (Awake(), Start(), etc) (2) | 2023.03.10 |
[Unity/C#] Singleton Pattern (싱글톤) (0) | 2023.02.14 |
- Total
- Today
- Yesterday
- unity
- async
- Visual Studio Code
- Debug
- await
- coroutine
- Singleton
- VS2022
- gcp
- Scraping
- github
- 유니티
- firestore
- Python
- Custom Package
- vscode
- git
- initialize
- 비동기
- 환경설정
- selenium
- 닷넷
- 코루틴
- .net
- framework
- RuntimeInitializeOnLoadMethod
- logging
- 싱글톤
- Addressables
- C#
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |