# 前言

最近项目在做 微信小游戏 版本,因此所有的 AssetBundle 都得加上 AppendHashToAssetBundleName ,据说主要是用于解决缓存问题。

例如文档

  • 【重要】BuildAssetBundleOptions.AppendHashToAssetBundleName:bundle 带上 hash。在小游戏底层对 bundle 做缓存及缓存淘汰时,hash 是重要依据。

—— 这也导致了我们打包的一个问题,所有的 AssetBundle 打完之后,实际的名字都被改变了 (而且可以预期的是每次资源发生变化都会变):

如果依照正常的名字去 AssetBundle.LoadFromFile("XXXX") 那肯定就是不行的了。

而且我们项目走的是自己的一套依赖管理,维护了一张自己的依赖表:

  • 首先计算出依赖关系,生成依赖表
  • 然后再根据计算的依赖表设置 AssetBundleName
  • 最后调用 BuildPipeline.BuildAssetBundles 打包。

也即是 先有依赖表,再有 AssetBundleName 资源 这种模式。

而非直接从 Unity 的 manifest 生成,所以存在当最终 AssetBundleName 文件名发生改变后,与自己依赖表中的对应名字对不上的情况。

比如像上面那样资源打包参数要是加上 AppendHashToAssetBundleName ,最终生成的 AssetBundle 文件名就会存在这样的问题 —— 因此就需要在资源打包完成后,读取 AssetBundleManifest 将自己先前的名称替换为最终带 Hash 的名字。

# 工具

Unity2021.2.5f1

# 经过

# 更改依赖表

最初以为是个很简单的处理,毕竟也两步而已:

  1. 读取生成后 .AssetBundleManifest
  2. 替换掉之前依赖表的常规名字
  3. 保存依赖表,并单打依赖表 AssetBundle

结果实际操作的时候,在第一步就为难了:有编辑器工具可以直接读取 AssetBundleManifest 文件吗?

找不到啊...

使用 AssetDatabase.LoadMainAssetAtPath 加载返回的竟然是 UnityEngine.DefaultAsset 类型的对象:

AssetDatabase.LoadMainAssetAtPath("Assets/StreamingAssets/StreamingResources/StreamingResources.manifest");
"StreamingResources (UnityEngine.DefaultAsset)"
    base: "StreamingResources (UnityEngine.DefaultAsset)"

自己研究了下,编辑器提供的 Editor 接口基本没有好用的,查了下几乎都说得先加载 AssetBundle 本身,然后再通过 LoadAsset<AssetBundleManifest> 使用:

AssetBundle assetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "StreamingResources/StreamingResources"));
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

而打包完资源,唯一没被附加 Hash 值导致改名的,唯有与目标目录同名的 主清单文件
这就没办法了,直接加载 AssetBundleManifest 的想法最终放弃,采用加载主清单文件,生成一个字典,然后对比自己的依赖表给改过去。
注:主清单 AssetBundleManifest 信息在调用打包时也会直接返回

主要代码如下:

public static bool BeginReadPackmanifestNames()
{
    AssetBundle bundle = AssetBundle.LoadFromFile(string.Concat(Application.dataPath, "/../", EditorPackDef.AssetBundleCacheDir, "/", EditorPackDef.AssetBundleCacheDir));
    if (bundle == null)
    {
        EditorUtility.DisplayDialog("错误", "未找到依赖总表!", "错误!");
        return false;
    }
    _packHashNamesDic = new Dictionary<string, string>(14000);
    AssetBundleManifest manifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
    string[] allAssets = manifest.GetAllAssetBundles();
    //string name;
    int num;
    bool haveTail;
    const string tail = ".assetbundle";
    foreach (var item in allAssets)
    {
        haveTail = item.EndsWith(tail);
        num = item.Length - (haveTail ? 45 : 33);
        if (num < 1) continue;
        _packHashNamesDic[string.Create(num + (haveTail ? tail.Length : 0), (item, num, haveTail, tail), (str, v) =>
        {
            v.item.AsSpan().Slice(0, num).CopyTo(str);
            if (haveTail) v.tail.AsSpan().CopyTo(str.Slice(num));
        })] = item;
    }
    return true;
}

也可以有其它方式:比如直接解析文本,但是那种感觉更不大好。

# 二次打包报 Warning

提示如下:

Assets in StreamingAssets cannot be included in AssetBundles: "XXXXXX"

不清楚是不是我们升级了 Unity 版本的问题?

毕竟是我直接将其从 Unity2018 升级到 Unity2021.2.5 的..... 公司其它项目说没碰到过,而之前 Unity2018 也没碰到过,已经没法确认是不是版本升级导致的了。

经过反复测试,发现只要 StreamingAssets 存在 .assetbundle 文件,就会直接报上述 Warning(虽然最终文件看着没什么问题,该重打的看着还是被重新更新了)

后面同事建议将所有 AssetBundle 打包至 Unity 工程目录外,然后再复制进 StreamingAssets

# 问题

所以,除了先加载 AssetBundle,然后通过 assetBundle.LoadAsset<AssetBundleManifest> 以及直接纯文本读取方式外,Unity 还有其它方式加载 AssetBundleManifest 吗?


  • 参考文档:
    • How to get AssetBundleManifest