Unity3D - 使用UnityWebRequest加载AssetBundle和Texture,进行缓存的方法

暂存一些链接: https://blog.csdn.net/qq_15267341/article/details/76803416

最后补充了一些我自己写的缓存代码, 思路是先把资源包下载到本地, 在用UnityWebRequest从本地加载, 这种方法的前提是各资源包独立, 没有关联关系.


最近有需求,对下载的AssetBundle进行缓存。Uniy 3D中提供的WWW.LoadFromCacheOrDownload这种自动缓存AssetBundle的类已经被标记为过时的了, 官方推荐使用UnityWebRequest类来下载资源包, 但是在网上找了好久也没发现使用这种方法如何缓存资源包.

某次看UnityWebRequest的文档的过程中发现, 下载资源包之后, 使用DownloadHandlerAssetBundle.assetBundle来加载资源包, 可以达到的AssetBundle.LoadFromFile的效率。参见下面的引用。

UnityWebRequest

You can also use the assetBundle property on the DownloadHandlerAssetBundle class after downloading the bundle to load the AssetBundle with the efficiency of AssetBundle.LoadFromFile.

 接着阅读DownloadHandlerAssetBundle的文档。这是一个DownloadHandler的子类,专门用来下载资源包的。这个子类使下载的数据变成流,输送到资源包编码和解压程序中。为资源包对象提供高效的下载和处理。

DownloadHandlerAssetBundle

Description

DownloadHandler subclass specialized for downloading AssetBundles.

This subclass streams downloaded data into Unity's asset bundle decompression and decoding system on worker threads, providing efficient downloading and processing for AssetBundle objects.

 本文结论:经过在Unity Manual中搜索,找到以下结果。

(引用自Creating DownloadHandlers, 章节:DownloadHandlerAssetBundle

DownloadHandlerAssetBundle

The advantage to this specialized Download Handler is that it is capable of streaming data to Unity’s AssetBundle system. Once the AssetBundle system has received enough data, the AssetBundle is available as a UnityEngine.AssetBundle object. Only one copy of the UnityEngine.AssetBundle object is created. This considerably reduces run-time memory allocation as well as the memory impact of loading your AssetBundle. It also allows AssetBundles to be partially used while not fully downloaded, so you can stream Assets.

All downloading and decompression occurs on worker threads.

AssetBundles are downloaded via a DownloadHandlerAssetBundle object, which has a special assetBundle property to retrieve the AssetBundle.

Due to the way the AssetBundle system works, all AssetBundle must have an address associated with them. Generally, this is the nominal URL at which they’re located (meaning the URL before any redirects). In almost all cases, you should pass in the same URL as you passed to the UnityWebRequest. When using the High Level API (HLAPI), this is done for you.

Example

using UnityEngine;
using UnityEngine.Networking; 
using System.Collections;
 
class MyBehaviour: MonoBehaviour {
    void Start() {
        StartCoroutine(GetAssetBundle());
    }
 
    IEnumerator GetAssetBundle() {
        UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
        DownloadHandlerAssetBundle handler = new DownloadHandlerAssetBundle(www.url, uint.MaxValue);
        www.downloadHandler = handler;
        yield return www.Send();
 
        if(www.isError) {
            Debug.Log(www.error);
        }
        else {
            // Extracts AssetBundle
            AssetBundle bundle = handler.assetBundle;
        }
    }
}

没有什么好解释的,有需要的话,直接复制以上代码。 

在同一个文章中,还有缓存Texture的方法,在这里也一并拷贝过来收藏。

我大概解释一下下面的代码:首先获取了一个Image(UI组件,没有学过UGUI的话直接在Hierarchy视图中新建UI->Image,将此脚本附加在Image上面),然后开启协程下载图片,下载完成后获取Texture对象,然后封装成Sprite对象,Image组件将其显示出来。

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking; 
using System.Collections;
 
[RequireComponent(typeof(UnityEngine.UI.Image))]
public class ImageDownloader : MonoBehaviour {
    UnityEngine.UI.Image _img;
 
    void Start () {
        _img = GetComponent<UnityEngine.UI.Image>();
        Download("http://www.mysite.com/myimage.png");
    }
 
    public void Download(string url) {
        StartCoroutine(LoadFromWeb(url));
    }
 
    IEnumerator LoadFromWeb(string url)
    {
        UnityWebRequest wr = new UnityWebRequest(url);
        DownloadHandlerTexture texDl = new DownloadHandlerTexture(true);
        wr.downloadHandler = texDl;
        yield return wr.Send();
        if(!wr.isError) {
            Texture2D t = texDl.texture;
            Sprite s = Sprite.Create(t, new Rect(0, 0, t.width, t.height),
                                     Vector2.zero, 1f);
            _img.sprite = s;
        }
    }
}

 


自己写的代码

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using UnityEngine.Networking;
 
public class LocalAssetBundleManager : MonoBehaviour {
    public static LocalAssetBundleManager _instance;
 
    private LinkedList<AssetItem> assetList = new LinkedList<AssetItem>();
    private string assetFolder;
    private string assetFolderForInternet;
    private string recordFile;
    private bool flag_DownloadModel;
    private bool flag_LoadModelFromLocal;
    private bool flag_LoadModelFromInternet;
    public bool flag_GetModel;
    void Awake() {
        _instance = this;
        assetFolder = Application.persistentDataPath + "/assets";
        assetFolderForInternet = Config.baseFolderForInternet + "/assets";
        Debug.Log("资源包文件夹: " + assetFolder);
        if (!Directory.Exists(assetFolder)) {
            Directory.CreateDirectory(assetFolder);
        }
        recordFile = assetFolder + "/manifest.dat";
    }
 
    void Start() {
        ReadAssetListFromFile();
    }
 
    void Update() {
 
    }
 
    public AssetBundle bundle;
 
    /// <summary>
    /// 程序入口
    /// </summary>
    /// <param name="assetBundleName">资源包路径,至少有两段如"book1/cube002/cube"</param>
    /// <param name="id"></param>
    /// <param name="version">想要的版本号(一定是最新的)</param>
    /// <returns></returns>
    public IEnumerator GetModel(string assetBundleName, int id, int version) {
        // 查找ID是否存在于本地的AssetList中
        AssetItem theAsset = null;
        foreach (AssetItem item in assetList) {
            if (item.id == id) {
                theAsset = item;
                break;
            }
        }
        if (theAsset != null) {     // 若ID存在
            Debug.Log("本地存在" + assetBundleName);
            // 请求本资源的最新版本号
            if (version > theAsset.version || !theAsset.file.Equals(assetBundleName)) {
                // 版本号不够新 先下载模型到本地, 在从本地加载模型 TODO 需要检测错误
                yield return DownloadModel(assetBundleName);
                yield return LoadModelFromLocal(assetBundleName);
                if (!flag_LoadModelFromLocal) {     // 如果从本地加载失败就从网络上下载
                    yield return LoadModelFromInternet(assetBundleName);
                }
                // 更新版本号
                theAsset.version = version;
                theAsset.file = assetBundleName;
                SaveAssetListToFile();
 
                theAsset.assetbundle = bundle;
            } else {
                // 版本号足够, 尝试从本地加载
                yield return LoadModelFromLocal(assetBundleName);
                if (!flag_LoadModelFromLocal) {
                    yield return LoadModelFromInternet(assetBundleName);
                }
                theAsset.assetbundle = bundle;
            }
        } else {
            Debug.Log("本地不存在" + assetBundleName);
            // 版本号不够新 先下载模型到本地, 在从本地加载模型 TODO 需要检测错误
            yield return DownloadModel(assetBundleName);
            yield return LoadModelFromLocal(assetBundleName);
            // 将该模型保存到资源列表
            if (flag_LoadModelFromLocal) {
                AssetItem assetItem = new AssetItem();
                assetItem.version = version;
                assetItem.id = id;
                assetItem.file = assetBundleName;
                assetList.AddLast(assetItem);
                SaveAssetListToFile();
 
                assetItem.assetbundle = bundle;
            }
        }
 
    }
 
 
    /// <summary>
    /// 创建文件夹并下载四个文件
    /// </summary>
    /// <param name="assetsBundleName"></param>
    /// <returns></returns>
    private IEnumerator DownloadModel(string assetsBundleName) {
        Debug.Log("正在从网络上下载模型文件(尚未加载)");
        Config.Toast("正在从网络上下载模型文件(尚未加载)");
        flag_DownloadModel = false;
        string book_cube10278;           // 以样例字符串来命名变量
        string cube;
        GetLastTwoPaths(assetsBundleName, out book_cube10278, out cube);
        // 创建文件夹
        Debug.Log("即将创建文件夹 " + assetFolder + "/" + book_cube10278);
        if (!Directory.Exists(assetFolder + "/" + book_cube10278)) {
            Directory.CreateDirectory(assetFolder + "/" + book_cube10278);
        }
        // 下载第一个文件 book1/cube10278
        string from1 = Config.baseURL + "/AssetBundles/Android/" + book_cube10278 + "/" + GetLastPartOfPath(book_cube10278);
        string to1 = assetFolder + "/" + book_cube10278 + "/" + GetLastPartOfPath(book_cube10278);
        Debug.Log("正在下载" + from1 + "到" + to1);
        yield return DownloadFile(from1, to1);
        // 下载第二个文件 book1/cube10278.manifest
        string from2 = Config.baseURL + "/AssetBundles/Android/" + book_cube10278 + "/" + GetLastPartOfPath(book_cube10278) + ".manifest";
        string to2 = assetFolder + "/" + book_cube10278 + "/" + GetLastPartOfPath(book_cube10278) + ".manifest";
        Debug.Log("正在下载" + from2 + "到" + to2);
        yield return DownloadFile(from2, to2);
        // 下载第三个文件 book1/cube
        string from3 = Config.baseURL + "/AssetBundles/Android/" + book_cube10278 + "/" + cube;
        string to3 = assetFolder + "/" + book_cube10278 + "/" + cube;
        Debug.Log("正在下载" + from3 + "到" + to3);
        yield return DownloadFile(from3, to3);
        // 下载第四个文件 book1/cube.manifest
        string from4 = Config.baseURL + "/AssetBundles/Android/" + book_cube10278 + "/" + cube + ".manifest";
        string to4 = assetFolder + "/" + book_cube10278 + "/" + cube + ".manifest";
        Debug.Log("正在下载" + from4 + "到" + to4);
        yield return DownloadFile(from4, to4);
        flag_DownloadModel = true;
    }
 
    /// <summary>
    /// 从Internet路径上下载文件, 存入Local文件中
    /// </summary>
    /// <param name="internet"></param>
    /// <param name="local"></param>
    /// <returns></returns>
    private IEnumerator DownloadFile(string internet, string local) {
        WWW www = new WWW(internet);
        yield return www;
        File.WriteAllBytes(local, www.bytes);
    }
 
    /// <summary>
    /// 从本地加载模型
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <returns></returns>
    private IEnumerator LoadModelFromLocal(string assetBundleName) {
        flag_LoadModelFromLocal = false;
        string uri = assetFolderForInternet + "/" + assetBundleName;
        Debug.Log("正在从本地加载模型: " + uri);
        UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri, 0);
        Config.Toast("从本地加载模型" + uri);
        yield return request.Send();                // 任务: 抛出异常如何处理?
        try {
 
            Debug.Log(String.Format("本地模型{0}加载完成", assetBundleName));
            bundle = DownloadHandlerAssetBundle.GetContent(request);
            // GameObject obj = bundle.LoadAsset<GameObject>(GetLastPartOfPath(assetBundleName) + ".prefab");
            flag_LoadModelFromLocal = true;
        } catch (Exception e) {
            Debug.Log(e.Message);
            flag_LoadModelFromLocal = true;
        }
    }
 
    /// <summary>
    /// 从网上下载模型
    /// </summary>
    /// <param name="assetBundleName"></param>
    /// <returns></returns>
    private IEnumerator LoadModelFromInternet(string assetBundleName) {
        flag_LoadModelFromInternet = false;
        string uri = Config.baseURL + "/AssetBundles/Android/" + assetBundleName;
        Config.Toast("缓存不足, 直接从网络上下载并使用");
        UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri, 0);
        yield return request.Send();                // 任务: 抛出异常如何处理?
        try {
            bundle = DownloadHandlerAssetBundle.GetContent(request);
            GameObject obj = bundle.LoadAsset<GameObject>(GetLastPartOfPath(assetBundleName) + ".prefab");
            Debug.Log(String.Format("模型{0}下载完成", assetBundleName));
            flag_LoadModelFromInternet = true;
        } catch (Exception e) {
            Debug.Log(e.Message);
            flag_LoadModelFromInternet = true;
        }
    }
 
    /// <summary>
    /// 从文件读取资源列表
    /// </summary>
    private void ReadAssetListFromFile() {
        if (File.Exists(recordFile)) {
            string json = File.ReadAllText(recordFile);
            try {
                Assets assets = JsonUtility.FromJson<Assets>(json);
                assetList.Clear();
                for (int i = 0; i < assets.assets.Length; i++) {
                    assetList.AddLast(assets.assets[i]);
                }
            } catch {
                assetList.Clear();
            }
        } else {
            assetList.Clear();
        }
    }
 
    /// <summary>
    /// 将资源列表保存到文件
    /// </summary>
    private void SaveAssetListToFile() {
        StringBuilder sb = new StringBuilder();
        sb.Append("{\"assets\":[");
        foreach (AssetItem item in assetList) {
            sb.Append(item.ToString());
            sb.Append(',');
        }
        if (assetList.Count > 0) {
            sb.Remove(sb.Length - 1, 1);        // 删除最后一个逗号
        }
        sb.Append("]}");
        File.WriteAllText(recordFile, sb.ToString());
    }
 
    public void GetLastTwoPaths(string route, out string penultimate, out string last) {
        string[] strs = route.Split('/');
        last = strs[strs.Length - 1];
        penultimate = route.Substring(0, route.Length - last.Length - 1);
    }
 
    public static String GetLastPartOfPath(string route) {
        string[] strs = route.Split('/');
        return strs[strs.Length - 1];
    }
}
 
[Serializable]
public class Assets {
    public AssetItem[] assets;
}
 
[Serializable]
public class AssetItem {
    public int id;
    public string file;
    public int version;
    public AssetBundle assetbundle;     // 本变量只是引用加载内存中的资源包
 
    public override String ToString() {
        return String.Format("{{\"id\":{0},\"file\":\"{1}\",\"version\":{2}}}", id, file, version);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值