에셋 번들 종속성
에셋 번들 관리자

에셋 번들의 전문적인 활용

에셋 번들을 로드하는 데 사용할 수 있는 네 가지 API가 있습니다. 각 API의 동작은 번들이 로드되는 플랫폼과 에셋 번들을 만들 때 사용된 압축 방법(비압축, LZMA, LZ4)에 따라 다릅니다.

다음 네 가지 API를 사용할 수 있습니다.

AssetBundle.LoadFromMemoryAsync

AssetBundle.LoadFromMemoryAsync

이 함수는 에셋 번들 데이터가 포함된 바이트 배열을 사용합니다. 필요할 경우 CRC 값을 전달할 수도 있습니다(선택 사항). 번들이 LZMA로 압축된 경우 에셋 번들 로드 중에 압축을 해제합니다. LZ4로 압축된 번들은 압축된 상태로 로드됩니다.

다음은 이 메서드를 사용한 예제

using UnityEngine;
using System.Collections;
using System.IO;

public class Example : MonoBehaviour
{
    IEnumerator LoadFromMemoryAsync(string path)
    {
        AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
        yield return createRequest;
        AssetBundle bundle = createRequest.assetBundle;
        var prefab = bundle.LoadAsset<GameObject>("MyObject");
        Instantiate(prefab);
    }
}

하지만 이 방법은 LoadFromMemoryAsync를 사용할 수 있는 유일한 방법이 아닙니다. File.ReadAllBytes(path)를 바이트 배열을 가져오는 프로시저를 대체할 수 있습니다.

AssetBundle.LoadFromFile

AssetBundle.LoadFromFile

이 API는 로컬 스토리지에서 압축되지 않은 번들을 로드할 때 매우 효율적입니다. LoadFromFile은 번들이 압축되지 않았거나 청크(LZ4) 압축된 경우 번들을 디스크에서 직접 로드합니다. 전체가 압축된(LZMA) 번들을 이 메서드로 로드하면 번들을 메모리에 로드하기 전에 압축이 해제됩니다.

LoadFromFile를 사용한 예제

public class LoadFromFileExample extends MonoBehaviour {
    function 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);
    }
}

참고: Unity 5.3 이상이 설치된 Android 기기에서 이 API로 에셋 번들을 스트리밍 에셋 패스에서 로드하려고 하면 장애가 발생합니다. 해당 경로의 콘텐츠가 압축된 .jar 파일 안에 상주하게 되기 때문입니다. Unity 5.4 이상에서는 API 호출을 Streaming Assets과 함께 문제 없이 사용할 수 있습니다.

WWW.LoadFromCacheOrDownload

WWW.LoadFromCacheOrDownload

지원 제외 예정(UnityWebRequest 사용)

이 API는 에셋 번들을 원격 서버에서 다운로드하거나 로컬 에셋 번들을 로드하는 데 유용하며, UnityWebRequest API의 바람직하지 않는 구 버전입니다.

에셋 번들을 원격 위치에서 로드하면 에셋 번들이 자동으로 캐시에 저장됩니다. 에셋 번들이 압축된 경우, 워커 스레드가 시작되어 번들의 압축을 풀고 캐시에 저장합니다. 압축을 풀고 캐시에 저장한 번들은 AssetBundle.LoadFromFile과 똑같이 로드됩니다.

LoadFromCacheOrDownload를 사용한 예제

using UnityEngine;
using System.Collections;

public class LoadFromCacheOrDownloadExample : MonoBehaviour
{
    IEnumerator Start ()
    {
            while (!Caching.ready)
                    yield return null;

        var www = WWW.LoadFromCacheOrDownload("http://myserver.com/myassetBundle", 5);
        yield return www;
        if(!string.IsNullOrEmpty(www.error))
        {
            Debug.Log(www.error);
            yield return;
        }
        var myLoadedAssetBundle = www.assetBundle;

        var asset = myLoadedAssetBundle.mainAsset;
    }
}

에셋 번들의 바이트를 WWW 오브젝트에 캐시로 저장하는 데는 메모리가 많이 사용되므로, WWW.LoadFromCacheOrDownload를 사용하는 모든 개발자는 에셋 번들을 적은 양(몇 메가바이트)으로 유지하는 것이 좋습니다. 또한 모바일 디바이스처럼 메모리가 많지 않은 플랫폼에서 작업하는 개발자는 메모리 사용량이 급증하는 현상을 방지하기 위해 에셋 번들을 한 번에 하나씩만 다운로드하는 코드를 작성할 수 있습니다.

캐시 폴더에 파일을 더 저장할 공간이 없는 경우, LoadFromCacheOrDownload는 새 에셋 번들을 저장할 수 있는 충분한 공간이 확보될 때까지 최근에 사용된 에셋 번들을 캐시에서 반복적으로 삭제합니다. 하드 디스크가 가득 차거나 캐시에 저장된 모든 파일이 현재 사용 중이어서 공간을 확보할 수 없는 경우, LoadFromCacheOrDownload()는 Caching을 건너뛰고 파일을 메모리로 스트리밍합니다.

LoadFromCacheOrDownload를 적용하기 위해서는 버전 파라미터(두 번째 파라미터)를 변경해야 합니다. 함수에 전달된 에셋 번들 버전이 현재 캐시에 저장되어 있는 버전과 일치해야만 에셋 번들이 로드됩니다.

UnityWebRequest

UnityWebRequest

UnityWebRequest에는 에셋 번들을 처리하는 특정 API 호출이 있습니다. 시작하려면 UnityWebRequest.GetAssetBundle을 사용하여 웹 요청을 만들어야 합니다. 요청을 반환한 후 요청 오브젝트를 DownloadHandlerAssetBundle.GetContent(UnityWebRequest)로 전달합니다. 이 GetContent 호출은 해당 에셋 번들 오브젝트를 반환합니다.

번들을 다운로드한 후 assetBundle 프로퍼티를 DownloadHandlerAssetBundle 클래스에 사용하여 AssetBundle을 AssetBundle.LoadFromFile 만큼 효율적으로 로드할 수 있습니다.

다음은 게임 오브젝트 두 개가 포함된 AssetBundle을 로드하고 인스턴스화하는 방법의 예제입니다. 이 프로세스를 시작하려면 StartCoroutine(InstantiateObject())을 호출하기만 하면 됩니다.

IEnumerator InstantiateObject()

    {
        string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;        UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
        yield return request.Send();
        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
        GameObject cube = bundle.LoadAsset<GameObject>("Cube");
        GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");
        Instantiate(cube);
        Instantiate(sprite);
    }

UnityWebRequest를 사용하면 개발자가 다운로드된 데이터를 더 유연한 방법으로 처리하고 불필요한 메모리 사용을 잠재적으로 없앨 수 있다는 장점이 있습니다. UnityWebRequest는 UnityEngine.WWW 클래스보다 더 많이 사용되는 최신 API입니다.

에셋 번들(AssetBundles)에서 에셋(Asset) 로드

이제 에셋 번들을 성공적으로 다운로드했으므로, 마침내 에셋를 여러 개 로드할 수 있습니다.

일반 코드 스니핏:

T objectFromBundle = bundleObject.LoadAsset<T>(assetName);

T는 로드하려는 에셋 타입입니다.

에셋 로드 방법을 결정할 때 선택할 수 있는 몇 가지 옵션이 있습니다. LoadAssetLoadAllAssets와 각각에 Async를 추가한 LoadAssetAsync 또는 LoadAllAssetsAsync를 선택할 수 있습니다.

에셋 번들에서 에셋을 동기식으로 로드하는 방법은 다음과 같습니다.

단일 게임 오브젝트를 로드하려면 다음을 사용합니다.

GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);

모든 에셋을 로드하려면 다음을 사용합니다.

Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();

이전에 학습한 메서드에서는 로드 중인 오브젝트의 타입이나 오브젝트의 배열을 반환하지만, 비동기식 방법에서는 AssetBundleRequest를 반환합니다. 에셋에 액세스하고자 할 경우 이 작업이 완료될 때까지 기다려야 합니다. 에셋을 로드하려면 다음을 사용합니다.

AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName);
yield return request;
var loadedAsset = request.asset;

또는

AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request;
var loadedAssets = request.allAssets;

에셋을 다운로드한 후 곧바로 시작할 수 있습니다! 로드된 오브젝트는 Unity의 다른 모든 오브젝트처럼 사용할 수 있습니다.

에셋 번들 매니페스트(AssetBundle Manifests) 로드

에셋 번들 매니페스트를 로드하면 에셋 번들의 종속성을 다루는 경우에 매우 유용할 수 있습니다.

유용한 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));
}

이제 에셋 번들 또는 에셋 번들 종속성과 에셋을 로드하려고 하므로, 이렇게 로드된 에셋 번들을 모두 관리하는 방법에 대해 설명하겠습니다.

로드된 에셋 번들(AssetBundles) 관리

참고 항목: 로드된 에셋 번들 관리의 Unity 학습 튜토리얼

Unity에서는 오브젝트가 액티브 씬에서 제거될 경우 자동으로 언로드되지 않습니다. 에셋 정리는 특정 시간에 실행되고, 수동으로 실행할 수도 있습니다.

에셋 번들을 로드하고 언로드할 시기를 아는 것은 중요합니다. 에셋 번들을 잘못 언로드하면 메모리에 오브젝트가 중복으로 저장되거나 텍스처가 누락되는 등의 바람직하지 않은 상황이 발생할 수 있습니다.

에셋 번들 관리에 대해 알아야 하는 가장 중요한 것은

AssetBundle.Unload(bool)을 호출해야 하는 시기와 true 또는 false를 함수 호출에 전달해야 하는지 여부입니다.업로드는 에셋 번들을 언로드하는 함수로, 정적이지 않습니다. 이 API는 호출되는 에셋 번들의 헤더 정보를 언로드합니다. 인수는 에셋 번들에서 인스턴스화하는 모든 오브젝트도 언로드해야 하는지 나타냅니다.

AssetBundle.Unload(true)는 AssetBundle에서 로드된 모든 게임 오브젝트 및 해당 종속성을 언로드합니다. 복사된 게임 오브젝트(예: 인스턴스화된 게임 오브젝트)는 더 이상 AssetBundle에 속하지 않기 때문에 여기에 포함되지 않습니다. 이러한 경우가 발생하면 해당 AssetBundle에서 로드되고 여기에 여전히 속해 있는 텍스처가 씬의 게임 오브젝트에서 사라지고 Unity에서 누락 텍스처로 취급됩니다.

아래 그림과 같이 머티리얼 M이 에셋 번들 AB에서 로드된다고 가정해 보겠습니다.

AB.Unload(true)가 호출될 경우 액티브 씬에 있는 M의 모든 인스턴스도 언로드되고 제거됩니다.

AB.Unload(false)를 대신 호출할 경우 M과 AB의 현재 인스턴스 체인이 끊어집니다.

나중에 AB가 다시 로드되고 AB.LoadAsset()이 호출될 경우, Unity는 M의 기존 복사본을 새로 로드된 머티리얼에 다시 연결하지 않습니다. 대신 로드된 M의 복사본이 두 개 생깁니다.

AssetBundle.Unload(false)를 사용하면 일반적으로 이상적인 상황으로 이어지지 않습니다. 대부분의 프로젝트에서는 AssetBundle.Unload(true)를 사용하여 오브젝트가 메모리에 중복 저장되지 않게 할 수 있습니다.

대부분의 프로젝트에서는 AssetBundle.Unload(true)를 사용하고 오브젝트가 중복되지 않도록 하는 메서드를 채택해야 합니다. 일반적으로 다음 두 가지 메서드가 사용됩니다.

  • 애플리케이션 수명 중에 임시 에셋 번들이 언로드되는 시점(예: 레벨 간 또는 화면 로딩 중)을 분명히 정의하는 방법

  • 개별 오브젝트에 대해 레퍼런스 카운트를 유지하고 모든 구성 오브젝트가 사용되지 않을 때만 에셋 번들을 언로드하는 방법. 이 방법을 사용하면 애플리케이션이 메모리 중복 없이 개별 오브젝트를 언로드하고 다시 로드할 수 있습니다.

애플리케이션이 AssetBundle.Unload(false)를 사용해야 하는 경우 개별 오브젝트를 다음 두 가지 방법으로만 언로드할 수 있습니다.

  • 원치 않는 오브젝트에 대한 모든 레퍼런스를 씬과 코드에서 모두 제거하는 방법. 이 작업을 수행한 후 Resources.UnloadUnusedAssets를 호출합니다.

  • 씬을 추가하지 않고 로드하는 방법. 이 방법을 사용하면 현재 씬의 모든 오브젝트가 제거되고 Resources.UnloadUnusedAssets이 자동으로 호출됩니다.

에셋 번들, 종속성 또는 에셋 로딩을 직접 관리하고 싶지 않은 경우 에셋 번들 관리자가 필요할 수 있습니다.


에셋 번들 종속성
에셋 번들 관리자