# 前言
最近项目在做 微信小游戏
版本,因此所有的 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
# 经过
# 更改依赖表
最初以为是个很简单的处理,毕竟也两步而已:
- 读取生成后
.AssetBundleManifest
- 替换掉之前依赖表的常规名字
- 保存依赖表,并单打依赖表
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