キャッシュをダウンロード

ゲームを実行する際に追加でダウンロードしたデータの事を「キャッシュ」と呼びます
Google Playでは100MB以下のAPKファイルでないとアップロードできないので
キャッシュで個別にデータをダウンロードする事になります
キャッシュを使う場合はAssetBundleと言う形式を使います
今回は画像をキャッシュする方法を説明します

■AssetBundle作成用のスクリプトを用意してAssetBundle作成 編

1. Unityを起動、プロジェクト作成
プロジェクトは "AssetBundleBuild01" としました

2. 画像を選択し、Inspectorを見ると下の方にAssetBundleという項目があります

ここを選択すると、項目が表示されるので「New」を選択し、AssetBundleの名前を入力します。
今回は "asset_pack01" という名前にしました

なお、大文字は使えないようです

3. AssetBundle作成用のスクリプトを用意します。
今回は Assets フォルダの下に "Scripts/Editor" という階層のフォルダを作成し、"AssetBundleBuild.cs" というスクリプトを作成しました。


スクリプトの中身は下記になります。

using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

public class AssetBundleBuild
{

    [MenuItem("Expansion/Build AssetBundleData")]

    public static void Build()
    {
        string assetBundleDirectory = "./AssetBundleData";      // 出力先ディレクトリ
        string AssetBundleBase = "";

        // 出力先ディレクトリが無かったら作る
        if (!Directory.Exists(assetBundleDirectory))
        {
            Directory.CreateDirectory(assetBundleDirectory);
        }

        // AssetBundleのビルド
#if UNITY_STANDALONE_WIN
        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
#endif
#if UNITY_ANDROID
        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.Android);
#endif
#if UNITY_IOS
        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.iOS);
#endif

        // AssetBundleの名前 ハッシュ サイズを纏めたデータを作る
        string path = "AssetBundleData/";
        List<string> files = new List<string>(Directory.GetFiles(path, "*.", SearchOption.AllDirectories));
        files.RemoveAt(files.IndexOf(path + "AssetBundleData"));
        // jsonを保存
        File.WriteAllText(path + "AssetBundleData.json", "");
        // アセットバンドルのロード
        AssetBundle ab = AssetBundle.LoadFromFile(path + "AssetBundleData");
        AssetBundleManifest Manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        // アセット情報を取得
        for (int i = 0; i < files.Count; i++)
        {
            // ハッシュ値の取得
            Hash128 Hash = Manifest.GetAssetBundleHash(Path.GetFileName(files[i]));
            // ファイルサイズの取得
            FileInfo file = new FileInfo(files[i]);
            long size = file.Length;
            // 文字列に登録
            AssetBundleBase = Path.GetFileName(files[i]) + "/" + Hash + "/" + size;
            // jsonに書き込み
            File.AppendAllText(path + "AssetBundleData.json", AssetBundleBase + "\n");
            // 余分なファイルを削除
            File.Delete(files[i] + ".manifest");
        }
        // 余分なファイルを削除
        File.Delete(path + "AssetBundleData");
        File.Delete(path + "AssetBundleData.manifest");

        // ビルド終了を表示
        EditorUtility.DisplayDialog("アセットバンドルビルド終了", "アセットバンドルビルドが終わりました", "OK");
    }
}


5. 作成用のスクリプトを実行します。 さきほどのスクリプトを追加するとUnity画面上部のメニュー内に「Expansion」が追加されているので、「Expansion」→「Build AssetBundleData」を選択して下さい

成功しているとプログレスバーが表示され、その後に「アセットバンドルビルドが終了しました」というウィンドウが表示されます


6. Unityのプロジェクトを作成した場所をエクスプローラー等で開きます。
"AssetBundleData" というフォルダが作成されており、フォルダの中を見るとAssetBundle用のデータが作成されているはずです


AssetBundle用のデータが作成されていれば成功です
元の画像は削除しても構いません


■作成したAssetBundleをネットワーク環境で読み込み 編

1. 作成したAssetBundleを全てサーバーにアップロードします
今回はDropboxを使用します

2. 読み込み用のスクリプトを用意します
Assets フォルダの下に"Cache.cs" というスクリプトを作成しました
スクリプトの中身は下記になります

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Cache : MonoBehaviour
{
    // データが保存されているWebサイトアドレス
    public string BaseURL;
    public string DataURL;
    // アセットバンドル名
    public string AssetBundleName;

    // アセットバンドルデータ
    public static List<string[]> AssetBundleData = new List<string[]>();

    private TextAsset LoadFile;
    AssetBundle AssetData;

    public List<Sprite> AllImage = new List<Sprite>();
    public Image image;

    IEnumerator Start()
    {
        // キャッシュ削除
        //Caching.ClearCache();
        // キャッシュの場所
        Debug.Log(Caching.currentCacheForWriting.path);
        // オンライン状態ならダウンロード
        if (Application.internetReachability != NetworkReachability.NotReachable)
        {
            // Jsonデータを読み込み
            yield return StartCoroutine(JsonDownload());
            // キャッシュを読み込み(Falseにするとサイズだけ取得)
            yield return StartCoroutine(CacheDownload(AssetBundleName, true));
        }
        // データを読み込み
        Load();
    }

    // Jsonデータの読み込み
    IEnumerator JsonDownload()
    {
        string savePath = Path.Combine(Caching.currentCacheForWriting.path, "AssetBundleData.json");
        using (UnityWebRequest request = UnityWebRequest.Get(BaseURL))
        {
            // アセットバンドルの基盤データをダウンロードする
            request.downloadHandler = new DownloadHandlerFile(savePath);
            request.SendWebRequest();
            if (request.isHttpError || request.isNetworkError)
            {
                //エラー
                Debug.LogError(request.error);
                yield break;
            }
            // アセットバンドルのダウンロードが完了するまで待つ
            while (!request.isDone)
            {
                yield return null;
            }
        }
        // jsonを読み込み
        string[] JsonData = File.ReadAllText(savePath).Split('\n');
        for (int i = 0; i < JsonData.Length; i++)
        {
            AssetBundleData.Add(JsonData[i].Split('/'));
        }
    }

    // AssetBundleデータのダウンロード
    IEnumerator CacheDownload(string Name, bool Download)
    {
        // アセットバンドルの追加データをダウンロードする
        Hash128 Hash = HashData(Name);
        UnityWebRequest Request = UnityWebRequestAssetBundle.GetAssetBundle(DataURL, Hash, 0);
        Request.SendWebRequest();
        if (Request.isHttpError || Request.isNetworkError)
        {
            //エラー
            Debug.LogError(Request.error);
            yield break;
        }
        // アセットバンドルのダウンロードが完了するまで待つ
        while (!Request.isDone)
        {
            // キャッシュが存在しない
            if (!File.Exists(GetNewPath(Name, Hash.ToString()) + "/__data"))
            {
                // キャッシュをダウンロード
                if (Download)
                {
                    // ダウンロード済みのサイズ/最大サイズ
                    Debug.Log("Download: " + Request.downloadedBytes + "/" + Size(Name));
                }
                // サイズだけ取得する
                else
                {
                    // ファイルサイズ取得
                    Debug.Log("Size: " + Size(Name));
                    // ダウンロード処理を停止する
                    Request.Abort();
                    break;
                }
            }
            yield return null;
        }
        if (Download)
        {
            // キャッシュの保存先のアドレスを保存
            PlayerPrefs.SetString(Name, Hash.ToString());
        }
    }

    // データのロード
    public void Load()
    {
        // キャッシュを1度も保存してない場合は処理をしない
        if (!Directory.Exists(GetPath(AssetBundleName)))
        {
            Debug.Log("キャッシュがありません");
        }
        else
        {
            // キャッシュをロード
            AssetData = AssetBundle.LoadFromFile(GetPath(AssetBundleName) + "/__data");
            // キャッシュから画像を抽出
            AllImage.AddRange(AssetData.LoadAllAssets<Sprite>());
            // 画像を変更
            image.sprite = AllImage[0];
        }
    }

    // パス取得(保存済みのキャッシュ)
    string GetPath(string Name)
    {
        return Caching.currentCacheForWriting.path + "/" + Name + "/" + PlayerPrefs.GetString(Name);
    }

    // パス取得(新しいキャッシュ)
    string GetNewPath(string Name,string Hash)
    {
        return Caching.currentCacheForWriting.path + "/" + Name + "/" + Hash;
    }

    // ハッシュ値取得
    Hash128 HashData(string Name)
    {
        for (int i = 0; i < AssetBundleData.Count; i++)
        {
            if (string.Equals(AssetBundleData[i][0], Name))
            {
                return Hash128.Parse(AssetBundleData[i][1]);
            }
        }
        return Hash128.Parse("");
    }

    // データサイズ取得
    ulong Size(string Name)
    {
        for (int i = 0; i < AssetBundleData.Count; i++)
        {
            if (string.Equals(AssetBundleData[i][0], Name))
            {
                return ulong.Parse(AssetBundleData[i][2]);
            }
        }
        return 0;
    }
}
3. CacheのInspectorから
BaseURL(AssetBundleDataをダウンロードするURL)
DataURL(asset_pack01をダウンロードするURL)
AssetBundleName(AssetBundleの名前 今回は"asset_pack01")
を入力します

※Dropboxを使用する場合 URLの最後の「?dl=0」を「?dl=1」にします

これで画像が変更すれば成功です

処理としてはAssetBundleDataをダウンロードし そこからasset_pack01のハッシュ値を取得して
その後asset_pack01のキャッシュがあるかどうかを確認し 同じのがあればキャッシュからロード 無かったり違かったりした場合はダウ ンロードします
また オフラインならばキャッシュからロードします

今回は画像データをキャッシュ化しましたが テキストデータや音楽データもキャッシュ化する事ができます

キャッシュを削除したい場合は
Caching.ClearCache();
を実行します
※キャッシュを読み込む前に実行してください

ダウンロードしたいデータのサイズだけ取得する場合は
StartCoroutine(CacheDownload(AssetBundleName, false));
を実行します(後ろを「false」にする)

参考URL
Unity のAssetBundleを試してみる - エンジニア的な事を何かしらアウトプットしておくブログ
【Unity】 UnityでAssetBundleをダウンロードする方法すべてまとめ - LIGHT11
【Unity】 AssetBundleのManifestファイルに書かれている内容について - テラシュールブログ
【Unity】 AssetBundleのハッシュ値を計算する方法 - LIGHT11
【Unity】 UnityWebRequest で ファイルを保存する方法 - うにてぃブログ
[Unity 2018.2] AssetBundleのキャッシュを完全に理解する - Qiita
UnityWebRequest でダウンロードの進捗を取得する - Qiita

戻る