役演亭 -Yakuentei- Roleplay with your own characters.
  1. ホーム
  2. 技術記事
  3. ゲーム開発

Unity アセットバンドル (AssetBundle) 実例付き入門

公開日 (Published) : 更新日 (Modified) :

外部のアセットファイルを読み込み可能にする「アセットバンドル」機能の説明です。

【Unity】アセットバンドル 実例付き入門

アセットバンドル機能を使用することで、例えば DLC 等の実装で必要な外部アセットの読み込みや、リソースの動的確保・解放、さらに暗号化や埋め込みによる保護など、より柔軟なアセット管理ができるようになります。

目次

まえがき (対象読者など)

Unity では、原則、画像や音声ファイルなど (=アセット) を「あらかじめ」インポートしておかなければ、それらの素材を利用できるようになりません。しかし、これだと例えば DLC のように外部の画像・音声ファイルを追加で読み込む必要がある場合に困ります。

これを迂回するために用意されている機能が「アセットバンドル」(AssetBundle) で、「ストリーミングアセット」(StreamingAssets) と組み合わせて使われることが多い…ということは、アセットバンドルを利用したことのない開発者でもご存じかもしれません。

本記事で生成するアセットバンドルのイメージ

筆者もその 1 人でしたが、実際に自作ゲーム (光の絆の生徒達) で DLC を出すことにしたため、改めてアセットバンドル機能を調査し、利用することになりました。

しかし、公式ドキュメントに書かれている手順は一手間多いことに加え、(本記事執筆時点では) Google 検索上位の記事は中級者以上を想定したものが多いです。

そこで、これからアセットバンドルを使おうと考えている未経験者向けに、「すぐに動かすことのできる例」を重視した記事を執筆することにしました。

中級者以上が知りたいと思われる知識 (圧縮形式の違いや、ファイル以外からの読み込み方法など) は、Google 検索結果の上位に腐るほど有りますので、それらの記事に譲ります。

事前準備 (使用素材、プロジェクト環境など)

使用素材 (アセット)

別記事「Unity でプログラミングのみ (C# Script のみ) でゲームを作る方法」の 事前準備 - 使用素材のうち、画像系素材 (=ぴぽや倉庫様の素材のみ) を使用します。

素材の置き場所については、「次の次」で説明します。
(まずは次の「Unity プロジェクト環境」から先に準備して下さい)

Unity プロジェクト環境

別記事「Unity でプログラミングのみ (C# Script のみ) でゲームを作る方法」の C# Script の作成と Main Camera への割当まで済ませることで、本記事の C# Script を実行できる状態になります。

フォルダ・ファイルの作成および素材 (アセット) のインポート

上記「Unity プロジェクト環境」まで終わったら、Assets フォルダの直下に、以下のように 2 つのフォルダ (Editor, StreamingAssets) を作成します。

「使用素材 (アセット)」で言及した画像系素材 (2 ファイル) についても、Assets フォルダの直下に置きます。

フォルダ・ファイルの作成および素材 (アセット) のインポート場所 (Assets フォルダ直下)

また、たった今作成していただいた Editor フォルダの中に、さらに追加で C# Script を作成します。ファイル名は「Editor」(拡張子まで含めると Editor.cs) とします。

フォルダ・ファイルの作成および素材 (アセット) のインポート場所 (Assets → Editor フォルダ)

アセットバンドルの生成方法

公式ドキュメント記載の手順との違い

公式ドキュメントに書かれている手順を大まかに要約すると、以下のようになっています。

  1. アセットバンドルに含めたいアセットをマウス等で選択し、手作業でグループ化する。
  2. Editor フォルダ内にエディタースクリプトを作成する。
  3. 上記スクリプトの中に、アセットバンドル生成用の C# Scriptを書いて実行する。

しかし、実は手順 3. で作成する C# Script に、手順 1. のグループ化情報も一緒に記述可能です。つまり、公式ドキュメントの手順だとかえって面倒なのです。

手順 1. でわざわざ手作業するくらいなら、手順 3. の C# Script に一緒に書いてしまった方が楽ですし、自動化にもなります。そのためのスクリプトの例をこれから提示します。

アセットバンドルをビルドするためのエディタースクリプト (Editor.cs)

説明は後で行いますので、事前準備で作成した Editor.cs に以下のコードを丸ごと貼り付けて下さい。(下記のコード中に、既に上述の手順 1. と 3. の内容が両方記述されています)

using UnityEditor;

public class Editor
{
    [MenuItem("Assets/!!!!!!!!!! Build !!!!!!!!!!")]
    static void Build()
    {
        // ■ 1 ファイル 1 アセットバンドルの生成例
        // (bundle11111, bundle22222)
        BuildPipeline.BuildAssetBundles("Assets/StreamingAssets",
            new AssetBundleBuild[]
            {
                new AssetBundleBuild()
                {
                    assetBundleName = "bundle11111",
                    assetNames = new[]
                    {
                        "Assets/pipo_battlebg001.png",
                    },
                },
                new AssetBundleBuild()
                {
                    assetBundleName = "bundle22222",
                    assetNames = new[]
                    {
                        "Assets/pipo_sprites.png",
                    },
                },
            },
            BuildAssetBundleOptions.None,
            BuildTarget.StandaloneWindows64);

        // ■ 複数ファイル 1 アセットバンドルの生成例
        // (bundle33333)
        BuildPipeline.BuildAssetBundles("Assets/StreamingAssets",
            new AssetBundleBuild[]
            {
                new AssetBundleBuild()
                {
                    assetBundleName = "bundle33333",
                    assetNames = new[]
                    {
                        "Assets/pipo_battlebg001.png",
                        "Assets/pipo_sprites.png",
                    },
                },
            },
            BuildAssetBundleOptions.None,
            BuildTarget.StandaloneWindows64);
    }
}

Windows 以外の PC で本コードを動かす場合の注意

実は、アセットバンドルのファイル形式はプラットフォーム依存です。

マルチプラットフォーム対応のゲームであれば、アセットバンドルのファイルをプラットフォーム別に用意する必要があります。

BuildTarget.StandaloneWindows64

Windows で Unity を使用している方であれば (現在の Windows は事実上すべて 64 bit になっているため) 上記のコードのままで問題ありませんが、Mac や Linux など Windows 以外の環境で Unity を使用している場合は、BuildTarget.StandaloneWindows64 の部分を適切と思われる値に書き換えて下さい。

エディタースクリプト (C# Script) の実行方法

通常の C# Script (NewBehaviourScript.cs など) とは異なり、エディタースクリプトは Unity Editor のメニュー上から実行するようになっています。
(Assets/Editor の中の C# Script は、すべてエディタースクリプトとして扱われます)

アセットバンドルの作成はエディタースクリプト専用機能 (=ゲーム本体には組み込めない機能) のため、通常の C# Script として実行するとエラーになります。

[MenuItem("Assets/!!!!!!!!!! Build !!!!!!!!!!")]

ソースコード中に上記の記述があることに気付いた方もいると思いますが、これが Unity Editor のメニュー上に追加する階層、メニュー名を指定するための記述です。なので、他の既存のメニューと被らなければ自由に設定して構いません。

実際に「Assets」メニューの中に同名のメニューができているはずですので、実行してみましょう。

アセットバンドルのビルドの実行

実行後、プログレスバーなどが表示された後、StreamingAssets フォルダの中に以下のようなファイルが生成されるはずです。

ただし、実際に「アセットバンドル」として必要になるファイルは、ソースコード中でも指定している以下の 3 ファイルのみです。(例えば DLC としてユーザーに配信する際は、これらのファイルだけ配信すれば OK です)

様々なファイルが生成されますが、実際に必要になるファイルは bundle11111、bundle22222、bundle33333 のみです。

今回の C# Script で行っているグループ化内容

今回の事例では、説明用に、あえて 2 種類の例を両方とも作成しています。

本記事で生成するアセットバンドルのイメージ

実際には、各アセットの使用目的に応じて、以下のように切り替えて運用するのがおすすめです。

  • bundle11111 と bundle22222 が、1 ファイル 1 アセットバンドルの例です。例えば RPG のボスモンスター画像のように、特定の戦闘でしか使われない画像であれば、こちらのやり方にした方が、敵キャラクター毎の個別確保・解放が動的にできるので有利です。
  • bundle33333 が、複数ファイル 1 アセットバンドルの例です。例えば味方キャラクター画像のように、ゲームプレイ中はほぼ常駐する画像であれば、こちらのやり方にした方がメモリーの一括管理ができる (=ゲームの起動・終了時に一括処理しやすい) ので楽です。

bundle11111、bundle22222、bundle33333 とソースコードの対応は次のとおりです。

bundle11111、bundle22222、bundle33333 とエディタースクリプト (ビルド) の対応のイメージ

つまり、グループ化のための情報 (どのアセットバンドルに、どのアセットを埋め込むか) を、配列変数として BuildAssetBundles() メソッドに渡せば OK です。

出力先の StreamingAssets フォルダについて

ご存じの方も多いかもしれませんが、StreamingAssets フォルダには以下のような特徴があります。

  • 通常の画像・音声ファイル等を置いても、単なるファイルとしてしか認識されない。(画像、音声等の形式としては認識されない)
  • Unity でゲームをビルド (例えば Windows 用に exe 形式でビルド) した場合も、オリジナルのファイルが StreamingAssets フォルダとともにそのままコピーされるだけ。
  • ゲームをプレイするユーザー側で、StreamingAssets フォルダの中に自由にファイルを追加・編集してもらうことができる。

そのため、DLC のように後からファイルを追加する必要があるものや、多言語翻訳テキストのようにユーザーサイドで新言語対応してもらう場合などに、いちいちゲームをビルドしなおす必要がない (=プラグインや MOD のように自由に拡張できるようになる) という特性があります。

他にも、ゲームによってはセーブデータの保存先として利用される場合もあります。
(ただし、セーブデータのようにプログラム側で直接書き換えが必要になるものは Application.persistentDataPath の方に保存するのが推奨されています)

StreamingAssets とアセットバンドルの連携について

アセットバンドルも、DLC のような後付けプラグインを実現するために使われるので、通常はユーザーにダウンロードしてもらった DLC ファイル (実体はアセットバンドル) を、StreamingAssets フォルダの中に手動で追加してもらうか、Steam 等の配信プラットフォームの機能で StreamingAssets フォルダの中に追加ダウンロードさせる等の運用になります。

例:アセットバンドルのファイルを DLC として直接配信する場合

  • エディタースクリプト用のプロジェクトは、ゲーム本体のプロジェクトとは別にしておき、アセットバンドルのビルド専用プロジェクト (=ユーザーには配信しない) とする。
  • ゲーム本体の開発時は、StreamingAssets フォルダの中に適宜アセットバンドルのファイルを入れたり消したりすることで、DLC の有無を動作確認する。
  • 本番 (配信用) のゲームを配信する際には、StreamingAssets フォルダの中身を空にしてビルドする。

アセットバンドルからのアセットの読み込み方法

ここまでの手順で、アセットバンドルの作り方や、その運用の仕方などは説明してきました。

しかし、実際にゲーム本体側でアセットバンドルの中から画像や効果音などを取り出すことができなければお話になりません。

そこで、次は、ゲーム本体に入れる C# Script の方 (本記事では NewBehaviourScript.cs の方) で、アセットバンドルから読み込む方法の例を説明していきます。

プロジェクトを分ける必要性について

先ほどの文章での説明のとおり、実運用上は、アセットバンドルのビルド用と、ゲーム本体用でプロジェクトを分けた方が安全です。

なぜなら、せっかく DLC としてアセットバンドルを別枠で配信したかったのに、バニラ (ゲーム本体) に紛れ込んでしまったという事故が起こりかねないからです。
(※ Unity に詳しくてアセットがどのように埋め込まれるかを熟知しているのなら別)

本記事では、説明用に同じプロジェクト内で「ビルド」と「読み込み」の両方を行っていますが、余裕のある方は以下のスクリプトを別プロジェクト (エディタースクリプトも、大本の画像ファイルも無い方) で動かしてみると勉強になると思います。

アセットバンドルを読み込んで使用するスクリプト

説明は後で行いますので、事前準備で作成した NewBehaviourScript.cs に以下のコードを丸ごと貼り付けて下さい。

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    AssetBundle _bundle11111;
    AssetBundle _bundle22222;
    AssetBundle _bundle33333;

    void Start()
    {
        Camera.main.orthographicSize = 180;

        // ■ 1 ファイル 1 アセットバンドルの読出例
        // (bundle11111, bundle22222)
        _bundle11111 = AssetBundle.LoadFromFile(
            Application.streamingAssetsPath + "/bundle11111");
        Texture2D texture1 = _bundle11111.LoadAsset<Texture2D>(
            "Assets/pipo_battlebg001.png");
        AddTextureToScene(texture1, -160, +90);

        _bundle22222 = AssetBundle.LoadFromFile(
            Application.streamingAssetsPath + "/bundle22222");
        Texture2D texture2 = _bundle22222.LoadAsset<Texture2D>(
            "Assets/pipo_sprites.png");
        AddTextureToScene(texture2, +160, +90);

        // ■ 複数ファイル 1 アセットバンドルの読出例
        // (bundle33333)
        _bundle33333 = AssetBundle.LoadFromFile(
            Application.streamingAssetsPath + "/bundle33333");
        Texture2D texture3 = _bundle33333.LoadAsset<Texture2D>(
            "Assets/pipo_battlebg001.png");
        AddTextureToScene(texture3, -160, -90);

        Texture2D texture4 = _bundle33333.LoadAsset<Texture2D>(
            "Assets/pipo_sprites.png");
        AddTextureToScene(texture4, +160, -90);
    }

    void AddTextureToScene(Texture2D texture, int x, int y)
    {
        Sprite sprite = Sprite.Create(texture,
            new Rect(0, 0, texture.width, texture.height),
            new Vector2(0.5f, 0.5f), 1);
        SpriteRenderer render =
            new GameObject().AddComponent<SpriteRenderer>();
        render.sprite = sprite;
        render.transform.position = new Vector3(x, y);
    }

    void OnApplicationQuit()
    {
        _bundle11111.Unload(true);
        _bundle22222.Unload(true);
        _bundle33333.Unload(true);
    }
}

実行結果の確認

上記は通常の C# Script (ゲーム本体に入れる処理であることを想定) なので、Play ボタン (再生マーク) から実行します。

以下のように、4 つのゲームオブジェクトが動的に生成され、画像が表示されれば成功です。

アセットバンドルからの画像の読み込み結果

処理内容の説明

アセットバンドルの読み込みは、Resources.Load() などと同様に C# Script 経由で行う必要があります。

おおまかには、Resources.Load() の代わりに AssetBundle.LoadFromFile() と LoadAsset() に置き換えれば OK と理解すると良いでしょう。

bundle11111、bundle22222、bundle33333 と C# Script (読み込み) の対応のイメージ

LoadFromFile() と LoadAsset() の 2 つが必要な理由は、bundle33333 のように 1 つのアセットバンドルの中に複数のアセットが入っているケースもあるからです。

  • (バンドルのオブジェクト名).LoadAsset() の部分が、Resources.Load() に対応していると解釈するとイメージしやすいでしょう。

ちなみに、アセットバンドルの読み込み方法は、LoadFromFile() 以外にも、LoadFromMemory()、LoadFromStream() など多岐に渡りますが、StreamingAssets フォルダから直接読み込む場合で、かつ暗号化などの特別な処理を必要としないのであれば、LoadFromFile() を使うのが最も簡単です。

アセットバンドルの解放について

アセットバンドルは、Resources.Load() と異なり、読み込んだアセットをゲームの実行途中でも解放できる (つまりメモリを適宜節約できる) という特徴もあります。

今回の事例では、ゲームの終了時に明示的にメモリを解放するようにしていますが、メモリ消費量の大きなゲームでは、不必要になった画像などを適宜 Unload() を行うようにした方が、マシンスペックに優しいゲームになるでしょう。

void OnApplicationQuit()
{
    _bundle11111.Unload(true);
    _bundle22222.Unload(true);
    _bundle33333.Unload(true);
}

補足:Addressables とアセットバンドルの関係

Unity で、アセットバンドルの機能をベースに追加された比較的新しい仕組みに Addressables というものがあります。ある意味、アセットバンドルのラッパーです。

Addressables の利点の 1 つは、(見かけ上) アセットバンドルを作らなくても、個々のアセットを直接動的確保・解放できるようになることで、内部的にはアセットバンドルを裏で自動生成することで実現しています。

  • 注意点として、昔から存在する Resources.Load() は「見かけ上」リソースを動的に読み込んでいるだけで、実際にはゲーム起動の時点で読み込まれているため、一部のケース (常駐させる必要のある画像を扱う場合など) を除いては Unity 公式も推奨していません。

なので、もしアセットバンドルの使用動機が、(DLC のような外部ファイルを読み込むことではなく) 単にアセットの動的確保・解放だけが目的あれば、Addressables の方が簡単ですし、Unity 公式も推奨しています。

今後予想される Addressables の潮流について

Addressables は Unity の中でも比較的新しい機能のため、バージョンを追うごとに機能が拡充されつつあります。

おそらく、今後は DLC のように外部ファイルを扱う必要が出てくるケースでも、Addressables を通して容易に実現できるようになっていくと筆者は予想します。

ただし、本記事執筆時点の最新 LTS 版 (Unity 2021) では、Addressables は発展途上で扱いづらい (特に細かい設定が必要なときに GUI に依存しており、筆者のようにプログラミング重視の開発者だとアセットバンドルの API を直接叩いた方が小回りが利く) という印象を受けるので、Addressable 自体が未完成と言っても過言ではないと思います。

しかし、そのうち Addressables も技術的に成熟していくでしょうし、Unity 公式もアセットバンドルを直接扱うよりも Addressables を介して扱うことを推奨する流れになりつつありますので、将来的には Addressables に移行していく流れになると筆者は感じています。

おわりに

本記事では、アセットバンドルの具体的な使用例として、(主に DLC のように) 後付けの外部ファイルを読み込む想定の例を提示しました。

アセットバンドルを初めて使う開発者の方向けのとっかかりとしては最適な内容になったと思いますが、中上級者向けの細かい説明 (パラメーターの種類、他の読み込み方法など) は割愛していますので、より詳しい内容に関しては Web 上の他の記事に譲りたいと思います。

本記事が、これから、DLC の実装など何らかの動機でアセットバンドルを使用したいと考えているゲーム作者の役に立っていただければ幸いです。

主な更新履歴

「ゲーム開発」一覧

Unity でプログラミングのみ (C# Script のみ) でゲームを作る方法
プログラマーの方向けに、Unity で極力デザイナに頼らずゲームを作る方法を紹介します。
Unity の文字列描画 (TextMesh Pro) を C# Script のみで行う方法
Unity で極力デザイナに頼らず TextMesh Pro で文字列を描画する方法を紹介します。
Unity エディター拡張スクリプトで「手作業」を減らす・無くす
実際のゲーム開発で作成したエディター拡張スクリプトを例に自動化方法を説明します。
Unity の C# Script で「無」から音を産み出す方法
音声ファイル無し、スクリプトのみでゼロから音を作って鳴らす方法を紹介します。
Unity アセット暗号化 実例付き入門
AssetStudio 等のツールで素材をぶっこ抜かれないように保護する方法の基本説明です。
Unity アセットバンドル (AssetBundle) 実例付き入門
(現在開いているページです)
Unity の Time.deltaTime で測った時間がおかしい? (float 型に注意)
Unity で Time.deltaTime をもちいて時間計測を行う際の注意点を解説します。
【暫定】RPG Maker Unite のアドオンからゲーム動作に介入する方法
従来ツクールのように、既存ゲーム動作に介入するための手法を調べた暫定記事です。
【IL2CPP対応】RPG Maker Unite のアドオンからメソッド差替する方法
iOS など IL2CPP でのビルドが必要な場合でも使えるメソッド差替方法を紹介します。