에셋 번들을 로드하는 데 사용할 수 있는 두 개의 API가 있습니다.
이 API는 디스크에서 직접 파일 콘텐츠를 점진적으로 읽을 수 있기 때문에 로컬 스토리지에서 비압축 및 청크 압축 번들(LZ4)을 로드할 때 매우 효율적입니다. 이 방법을 사용하여 전체 파일 압축(LZMA)으로 압축된 파일을 로드하면 먼저 파일을 메모리로 완전히 압축 해제해야 하므로 효율성이 떨어집니다.
LoadFromFile
를 사용한 예제
using System.IO;
using UnityEngine;
public class LoadFromFileExample : MonoBehaviour
{
void Start()
{
var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
if (myLoadedAssetBundle == null)
{
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("MyObject");
Instantiate(prefab);
}
}
로드 프로세스 중에 앱을 차단하지 않고 동기화 버전 AssetBundle.LoadFromFileAsync
를 사용할 수도 있습니다.
에셋 번들이 실제로 로컬 파일에 저장되지 않은 경우 에셋 번들을 로드하는 데 사용할 수 있는 몇 가지 다른 방법이 있습니다. 예를 들면 AssetBundle.LoadFromMemoryAsync를 사용할 수 있습니다.
UnityWebRequestAssetBundle API는 에셋 번들을 다운로드, 캐싱 및 로딩하는 데 필요한 특수 웹 요청을 생성하기 위해 호출될 수 있습니다.
일반적으로 URL은 웹 서비스에 노출된 것처럼 파일을 가리키는 ‘https://’ 주소로 표시됩니다. 또한 직접 파일 시스템 액세스를 지원하지 않는 플랫폼의 로컬 데이터에 액세스하기 위해 ’file://’을 사용할 수도 있습니다.
이 요청은 DownloadHandlerAssetBundle.GetContent(UnityWebRequestAssetBundle)로 전달되어 로드된 에셋 번들에 대한 에셋 번들 오브젝트를 가져옵니다.
예를 들면 다음과 같습니다.
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class DownloadExample : MonoBehaviour
{
IEnumerator Start()
{
string uri = "https://myserver/myBundles/bundle123";
uint crc = 1098980; // Expected content CRC
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, crc);
yield return request.SendWebRequest();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
var loadAsset = bundle.LoadAssetAsync<GameObject>("Assets/Players/MainPlayer.prefab");
yield return loadAsset;
Instantiate(loadAsset.asset);
}
}
또는 Await가 지원되는 2023.1부터는 다음과 같습니다.
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class DownloadExample : MonoBehaviour
{
async void Start()
{
string uri = "https://myserver/myBundles/bundle123";
uint crc = 1098980; // Expected content CRC
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, crc);
await request.SendWebRequest();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
var loadAsset = bundle.LoadAssetAsync<GameObject>("Assets/Players/MainPlayer.prefab");
await loadAsset;
Instantiate(loadAsset.asset);
}
}
이 예시에서는 간단한 설명을 위해 코드에 하드코딩된 CRC 값을 보여 줍니다. 그러나 실제로는 에셋 번들을 다운로드하기 전에 에셋 번들에 대한 예상 CRC 값을 별도로 다운로드하거나 파일에서 가져옵니다. 에셋 번들 다운로드 무결성 및 보안을 참조하십시오.
참고: 이 코드를 호출할 때마다 전체 에셋 번들 콘텐츠를 다운로드하지 않으려는 경우 에셋 번들에 대한 캐싱을 활성화할 수 있습니다. UnityWebRequestAssetBundle.GetAssetBundle을 호출할 때 에셋 번들 해시를 전달하면 됩니다. 해시는 에셋 번들 매니페스트에서 사용할 수 있는데, 관련 내용은 이 섹션의 뒷부분에서 설명합니다. 해시는 요청하는 에셋 번들의 정확한 빌드에 대한 버전 식별자 역할을 합니다. 캐시에 대한 자세한 내용과 압축 관련 고려 사항은 에셋 번들 압축 및 캐싱을 참조하십시오.
이제 에셋 번들을 성공적으로 로드했으므로 에셋을 여러 개 로드할 수 있습니다.
일반 코드 스니핏:
T objectFromBundle = bundleObject.LoadAsset<T>(assetName);
T는 로드하려는 에셋 타입입니다.
에셋 로드 방법을 결정할 때 선택할 수 있는 몇 가지 옵션이 있습니다. LoadAsset
및 LoadAllAssets
와 각각에 Async를 추가한 LoadAssetAsync
또는 LoadAllAssetsAsync
를 선택할 수 있습니다.
에셋 번들에서 에셋을 동기식으로 로드하는 방법은 다음과 같습니다.
단일 에셋을 로드하려면(예: 프리팹의 루트 게임 오브젝트) 다음을 사용합니다.
GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);
모든 에셋을 로드하려면 다음을 사용합니다.
Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();
이렇게 하면 각 에셋의 모든 루트 오브젝트 배열이 반환됩니다.
이전에 표시된 메서드가 로드 중인 오브젝트 유형이나 오브젝트 배열을 반환하는 반면, 비동기 메서드는 AssetBundleRequest를 반환합니다. 에셋에 액세스하기 전에 이 작업이 완료될 때까지 기다려야 합니다. 비동기식으로 에셋을 로드하려는 경우 다음을 사용합니다.
AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName);
yield return request; // 또는 await request;
var loadedAsset = request.asset;
또는
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request; // 또는 await request;
var loadedAssets = request.allAssets;
에셋을 다운로드한 후 곧바로 시작할 수 있습니다! 로드된 오브젝트는 Unity의 다른 모든 오브젝트처럼 사용할 수 있습니다.
에셋 번들 매니페스트를 로드하면 에셋 번들의 종속성을 다루는 경우에 매우 유용할 수 있습니다.
유용한 AssetBundleManifest 오브젝트를 얻으려면 해당 추가 에셋 번들(들어가 있는 폴더와 이름이 같은 에셋 번들)을 로드하고 이 에셋 번들에서 타입이 AssetBundleManifest인 오브젝트를 로드해야 합니다.
매니페스트 자체를 로드하는 방법은 다른 에셋을 에셋 번들에서 로드하는 방법과 똑같습니다.
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
이제 위 예제의 매니페스트 오브젝트를 통해 AssetBundleManifest
API 호출에 액세스할 수 있습니다. 여기서 매니페스트를 사용해 해당 에셋 번들에 대한 정보를 얻을 수 있습니다. 이 정보에는 에셋 번들에 대한 종속성 데이터, 해시 데이터 및 배리언트 데이터가 포함됩니다.
이전 에셋 번들 종속성 섹션에서는 에셋 번들에서 에셋을 로드하려면 먼저 에셋 번들이 종속된 에셋 번들을 로드해야 한다고 설명했습니다. 매니페스트 오브젝트를 사용하면 종속성을 동적으로 찾아서 로드할 수 있으므로, 코드에서 모든 에셋 번들 이름과 그 관계를 명시적으로 하드코딩할 필요가 없습니다. “assetBundle”이라는 이름의 에셋 번들에 대한 모든 종속성을 로드하고자 한다고 가정해 보겠습니다.
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] dependencies = manifest.GetAllDependencies("assetBundle"); //Pass the name of the bundle you want the dependencies for.
foreach(string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));
}
이제 에셋 번들 또는 에셋 번들 종속성과 에셋을 로드하려고 하므로, 이렇게 로드된 에셋 번들을 모두 관리하는 방법에 대해 설명하겠습니다.
참고: Addressables 패키지는 에셋 번들, 종속성, 에셋의 로딩을 관리하기 위한 기존의 시스템을 제공합니다. Unity는 직접 에셋 번들을 관리하기보다 Addressables 패키지를 사용하는 것을 권장합니다.
참고 항목: 로드된 에셋 번들 관리의 Unity 학습 튜토리얼
Unity에서는 오브젝트가 액티브 씬에서 제거될 경우 자동으로 언로드되지 않습니다. 에셋 정리는 특정 시간에 실행되고, 수동으로 실행할 수도 있습니다.
에셋 번들을 언제 로드 및 언로드할지 알아야 합니다. 에셋 번들을 적절하지 않은 방식으로 언로드하면 오브젝트가 메모리에서 중복되거나 누락될 수 있습니다.
에셋 번들 관리에 대해 알아야 하는 가장 중요한 것은 AssetBundle.Unload(bool) 또는 AssetBundle.UnloadAsync(bool) 호출 시기와 함수 호출에 참 또는 거짓을 전달해야 하는지 여부입니다. Unload는 메모리에서 에셋 번들 헤더와 기타 데이터 구조를 제거하여 에셋 번들을 언로드하는 비정적 함수입니다. 인자는 이 에셋 번들에서 인스턴스화된 모든 오브젝트도 언로드할지 여부를 나타냅니다.
AssetBundle.Unload(true)
는 에셋 번들에서 로드된 모든 게임 오브젝트(및 해당 종속성)를 언로드합니다. 씬에 복사한 오브젝트(예: Instantiate
호출)는 여기에 포함되지 않는데, 복사한 데이터는 에셋 번들에 속하지 않기 때문입니다. 씬의 오브젝트가 에셋 번들에서 로드된 텍스처를 참조하는 경우(여전히 에셋 번들에 속해 있음), 이 호출로 인해 텍스처가 사라지고 Unity는 이를 누락된 텍스처로 처리합니다.
예를 들어 아래 그림과 같이 머티리얼 M이 에셋 번들 AB에서 로드되어 프리팹 P에서 사용된다고 가정해 보겠습니다.
AB.Unload(true)를 호출하면 활성 씬의 오브젝트에서 참조된 M의 인스턴스가 모두 제거됩니다.
대신 AB.Unload(false)를 호출하면 M의 인스턴스는 메모리에 남아 있습니다. 이 인스턴스는 원본 에셋 번들에 다시 연결되지 않는 독립 오브젝트가 됩니다.
나중에 AB가 다시 로드되면 Unity는 M의 기존 복사본을 에셋 번들의 머티리얼에 다시 연결하지 않습니다.
프리팹 P의 다른 인스턴스를 생성한 경우 M의 기존 복사본을 사용하지 않으며, 대신 M의 두 번째 복사본이 로드됩니다.
일반적으로 AssetBundle.Unload(false)
를 사용하면 중복된 오브젝트 및 기타 문제가 발생할 수 있습니다. 대부분의 프로젝트에서 이러한 문제를 방지하려면 AssetBundle.Unload(true)
를 사용해야 합니다. AssetBundle.Unload(true)
를 안전하게 호출하는 두 가지 일반적인 전략은 다음과 같습니다.
애플리케이션 수명 중에 임시 에셋 번들이 언로드되는 시점(예: 레벨 간 또는 화면 로딩 중)을 분명히 정의하는 방법
개별 오브젝트에 대해 레퍼런스 카운트를 유지하고 모든 구성 오브젝트가 사용되지 않을 때만 에셋 번들을 언로드하는 방법. 이 방법을 사용하면 애플리케이션이 메모리 중복 없이 개별 오브젝트를 언로드하고 다시 로드할 수 있습니다.
애플리케이션이 AssetBundle.Unload(false)
를 사용해야 하는 경우 개별 오브젝트를 다음 두 가지 방법으로만 언로드할 수 있습니다.
원치 않는 오브젝트에 대한 모든 레퍼런스를 제거합니다. 예를 들어, 로드된 다른 오브젝트에 해당 오브젝트를 가리키는 필드가 없도록 합니다. 이 작업이 완료되면 Resources.UnloadUnusedAssets를 호출합니다.
씬을 추가하지 않고 로드하는 방법. 이 방법을 사용하면 현재 씬의 모든 오브젝트가 제거되고 Resources.UnloadUnusedAssets이 자동으로 호출됩니다.