Unity AssetBundlesでモデルをダウンロードできるようにしてみた

Unityではモデルやテクスチャなどコード以外のデータをAssetBundlesという仕組みで管理できます。ユーザーが最新のデータをダウンロードすることでアプリのアップデートなしでモデルや素材の変更ができます。
2021.11.24

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Unityではモデルやテクスチャなどコード以外のデータをAssetBundlesという仕組みで管理できます。ユーザーが最新のデータをダウンロードすることでアプリのアップデートなしでモデルや素材の変更ができます。

概要

今回の記事でやること

前回作成した平面検出UnityアプリをAssetBundleに対応します。ボールのプレハブをサーバーからダウンロードするように変更して、アプリの更新なしでデータが変更されるか確認します。サーバーはPythonでローカル環境に構築します。

開発環境

  • OS: macOS Monterey
  • Unity Version:2020.3.20f1
  • C#エディター: Visual Studio 2019 for Mac
  • Python: 3.9.7

AssetBundle対応

AssetBundleを有効にする

AssetBundleを有効にするためにはコードを記述する必要があります。

  • ProjectからAssetsを選択 > [Create] > [Folder] > Editorフォルダを作成
  • Editorフォルダ内から [Create] > [C# Script] > ファイル名をCreateAssetBundleに変更

  • 作成したCreateAssetBundleをダブルクリックして以下のように変更して保存

CreateAssetBundle.cs

using System.IO;
using UnityEditor;
using UnityEngine;

public class CreateAssetBundles : MonoBehaviour
{
    [MenuItem("Assets/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        string assetBundleDirectory = "Assets/StreamingAssets";
        if (!Directory.Exists(Application.streamingAssetsPath))
        {
            Directory.CreateDirectory(assetBundleDirectory);
        }
        BuildPipeline.BuildAssetBundles(assetBundleDirectory,
        BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
    }
}

ボールのプレハブをAssetBundleで管理するように変更する

  • Shpereを選択 > Inspector最下部のAssetBundleドロップダウンを選択 > [New]を選択 > [testbundle]を入力

これでこのプレハブはAssetBundleでの管理対象になります。AssetBundleをビルドするとAssets/StreamingAssetsフォルダが生成されます。

  • メニューから [Assets] > [Build AssetBundles]

データをサーバーからダウンロードするプログラムを作成する

今回はローカル環境にサーバーを立てるのでPCのIPを調べておきます。

データをダウンロードしてきてGameObjectをロードするプログラムを作成して保存します。

BundleWebLoader.cs

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class BundleWebLoader : MonoBehaviour
{
    private string bundleUrl = "http://192.168.50.68:8000/assetbundles/testbundle";
    private string assetName = "Sphere";

    public IEnumerator GetBundle(Action<GameObject> callback)
    {
        UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(bundleUrl);

        yield return www.SendWebRequest();
        if (www.result != UnityWebRequest.Result.Success)
        {
            Debug.LogError("Failed to download AssetBundle");
            yield break;
        }
        else
        {
            AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www);
            callback(bundle.LoadAsset(assetName) as GameObject);
            bundle.Unload(false);
        }
    }
}

アプリ起動時にオブジェクトをダウンロードするようにプログラム変更

アプリ起動時にAssetBundleをサーバーからダウンロードするようにプログラムを変更します。

PlaneDetection.cs

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class PlaneDetection : MonoBehaviour
{
    ARRaycastManager raycastManager;
    BundleWebLoader loader;

    // ダウンロードしてきたSphere
    GameObject instantiate;

    public delegate void Callback(Action<GameObject> gameObject);

    private void Awake()
    {
        raycastManager = GetComponent<ARRaycastManager>();
    }

    void Start()
    {
        loader = new BundleWebLoader();

        // ダウンロード開始
        StartCoroutine(loader.GetBundle((GameObject obj) => {
            // ダウンロードしたGameObject
            instantiate = obj;
        }));
    }

    void Update()
    {
        if (Input.touchCount == 0 || Input.GetTouch(0).phase != TouchPhase.Ended)
        {
            return;
        }

        var hits = new List<ARRaycastHit>();
        if (raycastManager.Raycast(Input.GetTouch(0).position, hits, TrackableType.PlaneWithinPolygon))
        {
            var hitPose = hits[0].pose;
            // ダウンロードに成功していればインスタンス化
            if(instantiate != null)
            {
                Instantiate(instantiate, hitPose.position, hitPose.rotation);
            }
        }
    }
}

ローカルサーバーを準備する

AssetBundle内のファイルを配置する

サーバーを起動したいフォルダ内にStreamingAssetsフォルダをコピーして、フォルダ名をassetbundlesに変更します。

今回はPythonでローカル環境にサーバーを立てます。Terminalで任意フォルダに移動してから以下コマンドでサーバーを立ち上げます。ポートを指定しない場合、設定は8000になります。

python3 -m http.server

※事前にウェブブラウザでプログラム内で設定したURLにアクセスしてファイルがダウンロードできることを確認してください。

アプリの実行

アプリをビルドし実行します。スクリーンショットは平面にボールを置いた結果です。

プレハブを変更して、再度アップロードする

ここからは、プレハブのマテリアル変更がアプリの再インストールなしで適用されるのかを確認します。

  • メニューから [Assets] > [Create] > [Material] ファイル名をSphereMaterialに変更
  • Sphereを選択から Main MapsのAlbedo横の色を選択し任意の色に変更
  • SphereMaterialをSphereプレハブにドラッグ&ドロップするとマテリアルが適用されます

再度AssetBundleをビルドします。

  • メニューから [Assets] > [Build AssetBundles]

ビルドが終わったらサーバーを起動しているフォルダ内のファイルを入れ替えます。

色が変わるのを確認

アプリを再起動して平面にボールを置くと色が変わっているのが確認できました。

まとめ

AssetBundleを使えばアプリを更新せずにデータ変更できることが確認できました。アプリ配信後にデータを変更したい場面はありそうなので何かと使えそうです。

参考