Version: 2020.1
에셋 번들 종속성
AssetBundle compression

에셋 번들의 전문적인 활용

에셋 번들을 로드하는 데 사용할 수 있는 네 가지 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를 사용한 예제

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);
    }
}

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

UnityWebRequestAssetBundle

UnityWebRequestAssetBundle

The UnityWebRequestAssetBundle has a specific API call to deal with AssetBundles. To begin, you’ll need to create your web request using UnityWebRequestAssetBundle.GetAssetBundle. After returning the request, pass the request object into DownloadHandlerAssetBundle.GetContent(UnityWebRequestAssetBundle). This GetContent call will return your AssetBundle object.

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

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

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

에셋 번들(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) 관리

Note: The Addressable Assets package provides a ready-made system to manage loading Asset Bundes, dependencies, and Assets. Unity recommends using Addressables rather than managing AssetBundles yourself.

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

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

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

The biggest things to understand about AssetBundle management is when to call AssetBundle.Unload(bool) – or AssetBundle.UnloadAsync(bool) – and if you should pass true or false into the function call. Unload is a non-static function that will unload your AssetBundle. This API unloads the header information of the AssetBundle being called. The argument indicates whether to also unload all Objects instantiated from this AssetBundle.

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

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

Let’s assume Material M is loaded from AssetBundle AB as shown below and used in Prefab P.

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

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

If AB is loaded again later and AB.LoadAsset() is called, Unity will not re-link the existing copies of M to the newly loaded Material.

If you create another instance of Prefab P, it will not use the existing copy of M. Instead two copies of M are loaded.

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

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

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

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

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

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

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

에셋 번들 종속성
AssetBundle compression