WebGL AssetBundle 加载 缓存
更新:HHH   时间:2023-1-7


U3D的AssetBundle真的是博(坑)大精(坑)深啊

安卓的话要先打包到StreamingAssetPath中,安装后第一次运行,自动把Streaming 中的解压到PersistentDataPath中,因为persistent目录是在运行过一次才创建的。

这样,之后再更新资源就直接下载到persistent目录下了

但是,WebGL项目呢

这个就不存在什么streaming或者persistent了,因为不能加载本地路径

所以就只有缓存,加载包时就要用

WWW.LoadFromDownloadOrCache(url, version)

这样会把资源下载到缓存目录中,每次加载时会判断版本,相同则直接从本地磁盘加载,不然才从网络下载,更新本地磁盘中的缓存。

这里坑就来了,就是这个版本号。第一次加载还好,随便设置个数,因为本地没有么,直接下载了。以后呢,怎么判断本地的版本和服务器的版本呢?

之所以有这个问题,是因为这个版本号是加载时写在程序里的,比如说第一次下载后,会把各包加载时的这个版本号也记录在本地缓存里,下次再加载时,程序里设置的版本号会与本地缓存中之前记录的版本号对比,如果不同,则说明有更新,下载包覆盖本地缓存,再加载,如果一样则没有更新,不下载,直接加载本地缓存。

看了很多例子,都是简单的写个0或者1,哪怕服务器端资源更新了,如果这个版本号还是用的和上次一样的,就认为没有更新,也不会从网络下载。

那么得想办法动态的获取包的这个版本,那么

WWW.LoadFromDownloadOrCache(url, version)

这个重载方法就不合适了,因为这个version没找到办法获取……
文档里说的是这个version要自增,也就是新的version要大于老的才更新,不过看网上有人说只要版本号不同,就更新,无论是大于还是小于,这个没试过,不过也无所谓了。

看了一眼,这个加载方法的另一项重载是:

public static WWW LoadFromCacheOrDownload(string url, Hash228 hash, uint crc);

说明:以下只是目前的一个设想,没有实践

首先说最后一个参数crc,按文档里说的,这个值非0时则对比crc,目前不关心这个,就让它是0
最重要的就是第二个参数hash
文档解释:

hash    Hash228 which is used as the version of the AssetBundle.

对比这个可以相当于version版本。
那好,想办法得到各assetbundle包的Hash228

从manifest文件下手,打包之后会有个总的manifest文件,和你的包的根目录名同名
比如打包在StreamingAssets下,则其目录下有一个StreamingAssets.manifest文件

using(WWW www=WWW.LoadFromDownloadOrCache("..../StreamingAssets/StreamingAssets"))
{
        yield return www;
        AssetBundle ab=www.assetBundle;
        AssetBundleManifest abm=ab.loadAsset<AssetBundleManifest>("AssetBundleManifest");
        string[] bundleNames=abm.GetAllAssetBundles();
        foreach(string item in bundleNames)
        {
                Hash228 hash = abm.GetAssetBundleHash(item);
        }
}

如上,GetAllAssetBundles()可以得到所有包的包名
再用GetAssetBundleHash(包名)得到包对应的hash228

那么每次加载资源之前,先把包的Hash228获取到,再用

public static WWW LoadFromCacheOrDownload(string url, Hash228 hash, uint crc);

这样每次加载都会对比这个Hash228值,这个就相当于是版本了

我试了一下,在开发环境下,如果资源没有任何修改,反复打包,出来的这个Hash228是不变的,如果有修改,比如材质的颜色变化一下,prefab的scale变化一下之类的,再打出来的包,Hash228就变化了。

理论上说,这样LoadFromDownloadOrCache就可以判断出有没有更新了

所以,每次打开程序,首先加载总的manifest,把包和对应的Hash228存到一个地方,比如字典

Dictionary<string, Hash228> bundleHashDict = new Dictionary<string, Hash228>();
using(WWW www=WWW.LoadFromDownloadOrCache("..../StreamingAssets/StreamingAssets"))
{
        yield return www;
        AssetBundle ab=www.assetBundle;
        AssetBundleManifest abm=ab.loadAsset<AssetBundleManifest>("AssetBundleManifest");
        string[] bundleNames=abm.GetAllAssetBundles();
        foreach(string item in bundleNames)
        {
                Hash228 hash = abm.GetAssetBundleHash(item);
                bundleHashDict.Add(item, hash);
        }
}

要加载一个名叫“a.assetbundle"的包

string path = "...../a.assetbundle";
Hash228 hash=bundleHashDict["a.assetbundle"];
using(WWW www = WWW.LoadFromDownloadOrCache(path, hash, 0))
{
        yield return www;
        .........
}

如上,根据包名在字典里取得其之前获取并存入字典里的Hash228,作为LoadFromDownloadOrCache方法的第二个参数,理论上会与本地缓存最后一次更新下载时存储的包的Hash228(在上一次调用此方法时写入缓存的)进行对比,相同则不更新,不同则更新。

理论上如此,有待实践

返回游戏开发教程...