Version: 2023.2
言語: 日本語
Output of the Build
AssetBundle compression and caching

アセットバンドルを使いこなす

There are two different APIs that you can use to load AssetBundles.

AssetBundle.LoadFromFile

AssetBundle.LoadFromFile

This API is highly-efficient when loading uncompressed and chunk compressed bundles (LZ4) from local storage because it can read content of the file incrementally, directly from disk. Loading a file that is compressed with full file compression (LZMA) using this method is less efficient because it requires that the file be fully decompressed into memory first.

以下は 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);
    }
}

Rather than blocking your app during the load process you can also use the asynchronous version, AssetBundle.LoadFromFileAsync.

There are several other methods available to load an AssetBundle if it is not actually stored in a local file. For example you can use AssetBundle.LoadFromMemoryAsync.

UnityWebRequestAssetBundle

The UnityWebRequestAssetBundle API can be called to create a special web request for downloading, caching and loading AssetBundles.

Typically the URL would be a ‘https://’ address pointing to the file as exposed by a web service. It could also be ‘file://’ to access local data on platforms that do not support direct file system access.

This request can be passed into DownloadHandlerAssetBundle.GetContent(UnityWebRequestAssetBundle) to get the AssetBundle object for the loaded AssetBundle.

For example

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

or starting in 2023.1 with Await support:

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

For simplicity the example shows a CRC value that is hardcoded in the code. But in practice the expected CRC values for your AssetBundles would be downloaded separately, or retrieved from a file, prior to downloading the AssetBundles. See AssetBundle Download Integrity and Security.

Note: In order to avoid downloading the entire AssetBundle content each time this code is called you could enable caching for your AssetBundle. This is done by passing the AssetBundle hash when calling UnityWebRequestAssetBundle.GetAssetBundle. The hash is available from the AssetBundle Manifest, which is described later in this section. The hash acts as version identifier for the precise build of the AssetBundle that you are requesting. See AssetBundle compression and caching for details of the Cache and the associated considerations about compression.

Loading Assets from AssetBundles

Now that you’ve successfully loaded your AssetBundle, it’s time to finally load in some Assets.

汎用コード スニペット

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

T は、読み込むアセットのタイプです。

アセットの読み込み方法はいくつかあります。 LoadAssetLoadAllAssets および、それらの Async バージョンである LoadAssetAsyncLoadAllAssetsAsync` です。

アセットバンドルから同期的にアセットを読み込む方法は以下の通りです。

To load a single Asset (for example the root GameObject of a Prefab):

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

全てのアセットを読み込むには以下を使用します。

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

This returns an array with all the root Object of each Asset.

Now, whereas the previously shown methods return either the type of object you’re loading or an array of objects, the asynchronous methods return an AssetBundleRequest. You’ll need to wait for this operation to complete before accessing the asset. To load an asset asynchronously:

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

または

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

アセットの読み込みが終わったら、準備完了です。読み込まれたオブジェクトを、その他のオブジェクトと同じように Unity 上で使用することができます。

AssetBundle Manifest を読み込む

アセットバンドル マニフェストを読み込むと非常に役立ち得ます。特にアセットバンドルの依存関係を扱う場合には有益です。

使用可能な AssetBundleManifest オブジェクトを取得するには、追加のアセットバンドル (その格納されているフォルダーと同じ名前のアセットバンドル) を読み込み、 AssetBundleManifest タイプのオブジェクトを 1 つ、そこから読み込む必要があります。

マニフェスト自体の読み込みは、その他通常のアセットをアセットバンドルから読み込む場合と全く同じように行われます。

AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

これで、上記の例にあるマニフェストオブジェクトによって AssetBundleManifest API コールが使用できるようになりました。ここからは、ビルドしたアセットバンドルの情報を、マニフェストを使用して取得することができます。この情報には、該当のアセットバンドルの依存関係データ、ハッシュデータ、バリアントデータが含まれます。

The earlier section on AssetBundle Dependencies discussed how, in order to load an Asset from an AssetBundle, you must first load any AssetBundles that the AssetBundle depends on. The manifest object makes dynamically finding and loading dependencies possible, so you do not have to hardcode all the AssetBundle names and their relationships explicitly in your code. Let’s say we want to load all the dependencies for an AssetBundle named “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));
}

これでアセットバンドル、アセットバンドルの依存関係、アセットの読み込みが完了しました。次は、読み込んだアセットバンドルの管理についての解説です。

読み込み済みアセットバンドルの管理

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

Unity Learn の 読み込み済みアセットバンドルの管理に関するチュートリアル も参照してください。

Unity は、アクティブなシーンから削除されたオブジェクトを自動的にアンロードしません。アセットのクリーンアップは特定の時点でトリガーされますが、手動でトリガーすることも可能です。

It is important to know when to load and unload an AssetBundle. Improperly unloading an AssetBundle can lead to duplicating objects in memory or missing objects.

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 by removing the AssetBundle header and other data structures from memory. The argument indicates whether to also unload all Objects instantiated from this AssetBundle.

AssetBundle.Unload(true) unloads all GameObjects (and their dependencies) that were loaded from the AssetBundle. This does not include objects that you have copied into your scene (e.g. by calling Instantiate) because that copied data does not belong to the AssetBundle. If the objects in your scene references Textures that were loaded from the AssetBundle (and still belong to it), then this call will cause the Textures to disappear, and Unity treats them as missing Textures.

As an example, let’s assume Material M is loaded from AssetBundle AB as shown below and used in Prefab P.

If AB.Unload(true) is called then any instance of M referenced from objects in the active scene will be destroyed.

If you were instead to call AB.Unload(false) then the instances of M will remain in memory. They will become independent objects that are not linked back to the original AssetBundle.

If AB is loaded again later Unity will not re-link the existing copies of M to the Material inside the AssetBundle.

If you create another instance of Prefab P, it will not use the existing copy of M. Instead a second copy of M is loaded.

Generally, using AssetBundle.Unload(false) can lead to duplicated objects and other issues. Most projects should use AssetBundle.Unload(true) to avoid this. There are two common strategies for safely calling AssetBundle.Unload(true):

  • アプリケーションのライフタイム内で、一時的なアセットバンドルをアンロードする丁度良いタイミングを設定する。例えばステージとステージの間やロード画面など。

  • 個々のオブジェクトの参照カウントを維持し、アセットバンドルに含まれるオブジェクトがひとつも使用中でない場合にのみ、アセットバンドルをアンロードする。この方法だと、メモリの重複を起こすことなく、アプリケーションのオブジェクトを個々にアンロードおよび再読み込みできます。

どうしても AssetBundle.Unload(false) の使用が必要なアプリケーションの場合、個々のオブジェクトをアンロードする方法は次の 2 つのみになります。

  • Eliminate all references to an unwanted Object, e.g. make sure no other loaded object has a field that points to it. After this is done, call Resources.UnloadUnusedAssets.

  • 非加算的にシーンを読み込む。これにより、現在のシーン内の全てのオブジェクトが破棄されて Resources.UnloadUnusedAssets が自動的に実行されます。

Output of the Build
AssetBundle compression and caching