비동기 작업 핸들
어드레서블의 많은 작업에서는 정보를 로드하거나 다운로드해야 결과를 반환할 수 있습니다. 프로그램 실행이 차단되지 않도록 어드레서블은 비동기 작업과 같은 작업을 구현합니다.
동기 작업은 결과를 확인할 수 있을 때까지 컨트롤을 반환하지 않는 반면, 비동기 작업은 거의 즉각적으로 호출 메서드에 컨트롤을 반환합니다. 하지만 결과는 어느 정도 시간이 지나야 확인할 수 있습니다.
LoadAssetAsync
와 같은 메서드를 호출해도 로드된 에셋이 바로 반환되지 않습니다. 대신 AsyncOperationHandle
오브젝트를 반환하며, 이를 통해 로드된 에셋이 사용 가능해지면 해당 에셋에 액세스할 수 있습니다.
다음과 같은 기술을 사용하여 다른 스크립트가 처리를 계속하도록 하면서 비동기 작업의 결과를 기다릴 수 있습니다.
[!참고] 현재 스레드를 차단하여 비동기 작업이 완료될 때까지 기다릴 수 있습니다. 이 경우 성능 문제가 발생하고 프레임 속도가 저하될 수 있습니다. 자세히 알아보려면 비동기식으로 작업 사용을 참조하십시오.
AsyncOperationHandle 인스턴스 해제
LoadAssetsAsync
와 같은 메서드는 작업 결과와 결과 및 작업 오브젝트 자체를 해제하는 방법을 제공하는 AsyncOperationHandle
인스턴스를 반환합니다.
결과를 사용하려는 기간에는 핸들 오브젝트를 유지해야 합니다. 상황에 따라 한 프레임이 될 수도 있고 하나의 레벨이 끝날 때까지일 수도 있으며, 애플리케이션의 수명 주기 내내일 수도 있습니다. 작업 핸들 및 관련 어드레서블 에셋을 해제하려면 Addressables.Release
메서드를 사용하십시오.
작업 핸들을 해제하면 작업을 통해 로드된 모든 에셋의 레퍼런스 수가 감소하고 작업 핸들 오브젝트 자체가 무효화됩니다. 어드레서블 시스템의 레퍼런스 수에 대한 자세한 내용은 메모리 관리를 참조하십시오.
제한된 범위 이외의 작업 결과를 사용할 필요가 없는 경우 핸들을 즉시 해제할 수 있습니다. UnloadSceneAsync
와 같은 일부 어드레서블 메서드는 작업이 완료된 경우 자동으로 작업 핸들을 해제할 수 있게 합니다.
작업이 실패한 경우에도 작업 핸들을 해제해야 합니다. 어드레서블은 실패한 작업 중에 로드한 모든 에셋을 해제하지만, 핸들을 해제해도 핸들의 인스턴스 데이터는 삭제됩니다. LoadAssetsAsync
와 같이 여러 에셋을 로드하는 일부 메서드는 로드한 에셋을 유지하거나 로드 작업이 실패할 경우 모든 에셋을 실패 처리하고 해제할 수 있는 옵션을 제공합니다.
코루틴 및 IEnumerator 작업 처리
AsyncOperationHandle
은 IEnumerator
인터페이스를 구현하며 작업이 완료될 때까지 반복 작업을 계속합니다.
코루틴에서는 작업 핸들을 반환하여 다음 반복 작업을 기다릴 수 있습니다. 완료되면 실행 플로는 다음 문으로 이어집니다. MonoBehaviour Start
메서드를 코루틴으로 구현할 수 있으며, 이 경우 게임 오브젝트를 로드하고 필요한 에셋을 인스턴스화할 수 있습니다.
다음 스크립트는 코루틴에서 Start
메서드를 사용하여 프리팹을 게임 오브젝트의 자식으로 로드합니다. 작업이 완료될 때까지 AsyncOperationHandle
을 반환한 다음 동일한 핸들을 사용하여 프리팹을 인스턴스화합니다.
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);
// yielding when already done still waits until the next frame
// so don't yield if done.
if (!opHandle.IsDone)
yield return opHandle;
if (opHandle.Status == AsyncOperationStatus.Succeeded)
{
Instantiate(opHandle.Result, transform);
}
else
{
Addressables.Release(opHandle);
}
}
void OnDestroy()
{
Addressables.Release(opHandle);
}
}
작업이 시작되면 Addressables.LoadAssetsAsync
를 취소할 수 없습니다. 그러나 완료되기 전에 핸들을 해제하면 핸들 레퍼런스 수가 감소하며 로드가 완료되면 자동으로 핸들이 해제됩니다.
자세한 내용은 Unity 사용자 매뉴얼의 코루틴 기술 자료를 참조하십시오.
코루틴에서 작업 그룹화
레벨을 시작하기 전에 프리팹 및 기타 에셋을 로드하는 등 게임 로직의 다음 단계로 넘어가기 전에 여러 작업을 수행하려면, 모든 작업이 에셋을 로드하는 경우 Addressables.LoadAssetsAsync
메서드에 대한 호출 한 번으로 작업을 결합할 수 있습니다.
이 메서드에 대한 AsyncOperationHandle
은 LoadAssetAsync
와 동일하게 작동합니다. 코루틴에서 핸들을 반환하여 작업의 모든 에셋이 로드될 때까지 기다릴 수 있습니다. 또한 LoadAssetsAsync
에 콜백 메서드를 전달할 수도 있으며, 특정 에셋의 로드가 완료되면 작업이 해당 메서드를 호출합니다. 여러 에셋 로드에서 예시를 확인해 보십시오.
또한 ResourceManager.CreateGenericGroupOperation
을 사용하여 모든 작업을 마치면 완료되는 그룹 작업을 만들 수 있습니다.
이벤트 기반 작업 처리
AsyncOperationHandle
의 Completed
이벤트에 델리게이트 함수를 추가할 수 있습니다. 작업이 완료되면 델리게이트 함수가 호출됩니다.
다음 스크립트는 코루틴 및 IEnumerator 작업 처리의 예시와 등일한 기능을 수행하지만 코루틴 대신 이벤트 델리게이트를 사용합니다.
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadWithEvent : MonoBehaviour
{
public string address;
AsyncOperationHandle<GameObject> opHandle;
void Start()
{
// Create operation
opHandle = Addressables.LoadAssetAsync<GameObject>(address);
// Add event handler
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);
}
}
이벤트 델리게이트에 전달된 핸들 인스턴스는 원래 메서드 호출에서 반환된 것과 동일합니다. 둘 중 하나를 사용하여 작업의 결과 및 상태에 액세스하고 작업 핸들 및 로드된 에셋을 해제할 수 있습니다.
태스크 기반 작업 처리
AsyncOperationHandle
은 비동기 메서드를 호출하고 결과를 처리하는 코드를 배열하기 위해 C# async
및 await
키워드와 함께 사용할 수 있는 Task
오브젝트를 제공합니다.
다음 예시에서는 키 목록을 사용하여 어드레서블 에셋을 로드합니다. 태스크 기반 접근 방식과 코루틴 또는 이벤트 기반 접근 방식의 차이는 호출 메서드의 서명에 있습니다. 이 메서드에는 async
키워드가 포함되어야 하며, 작업 핸들의 Task
프로퍼티와 함께 await
키워드를 사용해야 합니다. 이 경우 호출 메서드인 Start
는 태스크가 완료되는 동안 작업을 일시 중지합니다. 그런 다음 실행이 재개되며, 예시에서는 로드된 모든 프리팹을 그리드 패턴으로 인스턴스화합니다.
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.
}
}
[!중요]
AsyncOperationHandle.Task
프로퍼티는 멀티태스킹을 지원하지 않는 WebGL 플랫폼에서 사용이 불가합니다.
Task
기반 작업 처리를 사용할 경우 WhenAll
과 같은 C# Task
클래스 메서드를 사용하여 병렬로 실행할 작업과 순서대로 실행할 작업을 제어할 수 있습니다. 다음 예시에서는 다음 태스크로 넘어가기 전에 두 개 이상의 작업이 완료되기를 기다리는 방법을 보여 줍니다.
// 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);
비동기식으로 작업 사용
반환하거나, 이벤트를 기다리거나, 작업의 WaitForCompletion
메서드를 호출하여 async await
를 사용하지 않고 작업이 완료되기를 기다릴 수 있습니다. 이 메서드는 기존의 프로그램 실행 스레드가 현재 범위에서 계속하기 전에 작업이 완료되기를 기다리는 동안 해당 스레드를 차단합니다.
데이터를 다운로드해야 하는 작업과 같이 상당한 시간이 소요될 수 있는 작업에서는 WaitForCompletion
을 호출하지 마십시오. WaitForCompletion
을 호출하면 프레임에 문제가 발생하고 UI 응답성이 저하될 수 있습니다.
다음 예시에서는 주소별로 프리팹 에셋을 로드하고 작업이 완료될 때까지 기다린 다음 프리팹을 인스턴스화합니다.
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
internal class LoadSynchronously : MonoBehaviour
{
public string address;
AsyncOperationHandle<GameObject> opHandle;
void Start()
{
opHandle = Addressables.LoadAssetAsync<GameObject>(address);
opHandle.WaitForCompletion(); // Returns when operation is complete
if (opHandle.Status == AsyncOperationStatus.Succeeded)
{
Instantiate(opHandle.Result, transform);
}
else
{
Addressables.Release(opHandle);
}
}
void OnDestroy()
{
Addressables.Release(opHandle);
}
}
커스텀 작업
커스텀 작업을 생성하려면 AsyncOperationBase
클래스를 확장한 다음 가상 메서드를 오버라이드합니다.
파생된 작업을 ResourceManager.StartOperation
메서드에 전달하여 작업을 시작하고 AsyncOperationHandle
구조체를 수신할 수 있습니다. ResourceManager
는 이렇게 시작된 작업을 등록합니다.
커스텀 작업 실행
선택적 종속 작업이 완료되면 ResourceManager
가 커스텀 작업을 위해 AsyncOperationBase.Execute
메서드를 호출합니다.
완료 처리
커스텀 작업이 완료되면 커스텀 작업 오브젝트에서 AsyncOperationBase.Complete
를 호출합니다. Execute
메서드에서 호출하거나 호출 외부로 지연시킬 수 있습니다. AsyncOperationBase.Complete
가 작업이 완료되었음을 ResourceManager
에 알립니다. ResourceManager
는 커스텀 작업의 관련 인스턴스에 대한 AsyncOperationHandle.Completed
이벤트를 호출합니다.
커스텀 작업 종료
AsyncOperationBase.ReferenceCount
작업이 0에 도달하면 ResourceManager
가 커스텀 작업을 위해 AsyncOperationBase.Destroy
메서드를 호출합니다. AsyncOperationBase.ReferenceCount
는 이를 참조하는 AsyncOperationHandle
이 Addressables.Release
를 사용하여 해제되거나 AsyncOperationBase.DecrementReferenceCount
가 내부에서 커스텀 작업에 의해 호출될 때 감소합니다. AsyncOperationBase.Destroy
에서 커스텀 작업과 관련된 메모리나 리소스를 해제해야 합니다.
입력 및 무입력 작업 핸들
작업을 시작하는 대부분의 Addressables
메서드는 일반 AsyncOperationHandle<T>
구조체를 반환하여 AsyncOperationHandle.Completed
이벤트 및 AsyncOperationHandle.Result
오브젝트에 대한 입력 안전성을 제공합니다. 또한 비일반 AsyncOperationHandle
구조체를 사용하고 두 개의 핸들 유형을 원하는 대로 전환할 수 있습니다.
비일반 핸들을 잘못된 유형의 일반 핸들로 캐스트하려고 하면 런타임 예외가 발생합니다. 예제:
// Load asset using typed handle:
AsyncOperationHandle<Texture2D> textureHandle = Addressables.LoadAssetAsync<Texture2D>("mytexture");
// Convert the AsyncOperationHandle<Texture2D> to an AsyncOperationHandle:
AsyncOperationHandle nonGenericHandle = textureHandle;
// Convert the AsyncOperationHandle to an AsyncOperationHandle<Texture2D>:
AsyncOperationHandle<Texture2D> textureHandle2 = nonGenericHandle.Convert<Texture2D>();
// This will throw and exception because Texture2D is required:
AsyncOperationHandle<Texture> textureHandle3 = nonGenericHandle.Convert<Texture>();
보고서 작업 진행
AsyncOperationHandle
에서는 다음 메서드를 사용하여 작업 진행 상황을 모니터링하고 보고할 수 있습니다.
GetDownloadStatus
:DownloadStatus
구조체를 반환합니다. 이 구조체에는 다운로드된 바이트 수와 아직 다운로드해야 하는 바이트 수에 대한 정보가 포함되어 있습니다.DownloadStatus.Percent
에서 다운로드된 바이트의 백분율을 보고합니다.AsyncOperationHandle.PercentComplete
: 완료된 모든 하위 작업에 대해 동일한 가중치가 적용된 총 백분율을 반환합니다. 예를 들어 하나의 작업에 5개의 하위 작업이 있는 경우 각 하위 작업은 전체의 20%를 차지합니다. 개별 하위 작업에서 다운로드해야 하는 데이터의 양은 이 값에 영향을 미치지 않습니다.
예를 들어 Addressables.DownloadDependenciesAsync
를 호출하고 5개의 에셋 번들을 다운로드해야 하는 경우, GetDownloadStatus
는 모든 하위 작업의 총 바이트 수 대비 다운로드된 바이트 수의 백분율을 알려 줍니다. PercentComplete
는 작업의 크기에 관계 없이 완료된 작업 수의 백분율을 알려 줍니다.
LoadAssetAsync
를 호출했으며 하나의 번들을 다운로드해야 에셋을 로드할 수 있는 경우, 정확한 다운로드 백분율을 구하기 어려울 수 있습니다. 수행해야 할 추가 하위 작업이 있으므로 GetDownloadStatus
에서 얻은 값은 작업이 완료되기 전에 100%에 도달합니다. PercentComplete
의 값은 다운로드 하위 작업이 완료되면 50%, 실제 메모리 로드가 완료되면 100%가 됩니다.