首先放源码链接,addressable source code.
Addressable(以下简称AA),是Unity最新推出的资源管理方案,它与之前的AssetBundle(以下简称AB)不是替代关系, 而是建立在AssetBundle提供的基础功能上面的一套更加封装和易用的系统。
在之前使用AB的时候,由于AB提供的接口非常的基础和底层,这虽然有很高的自由度,但更常见的情况就是资源系统的 的每一处细节都要开发人员自己去实现,事无巨细,非常麻烦,bug也多。这也导致涌现出了很多资源管理框架。
后来Unity也推出了Addressable,在写下这篇文章的时候,最新版本是1.20.5,大概每两个月左右一个版本, 还处于活跃开发阶段,会修复很多问题或者增加一些功能。
按照Unity的描述 AA可以”Simplify your content management with Addressables”。实际上也是这样, AA为开发者封装了很多细节,包括依赖管理,引用计数,打包,下载,更新等等。如果没有很特殊需求, 按照AA的教程,很快就能搭起一套可用的完全体资源管理系统。
AA可以在PackageManager中直接安装。建议可以直接转最新的版本,因为所谓的稳定版本其实也不是那么稳定, 我曾在项目中使用稳定版本,结果一个API调用会在真机上产生竞态条件卡死,导致不得不使用魔改过的版本, 六个月后的新版本中Unity的团队才修复了这个问题。
本文不是一个教程,如果从0开始学习可以移步官方文档
下面是几个在实际使用中遇到的坑点。
Addressables.CheckForCatalogUpdates()
private ResourceLocationMap GetCatalogMap(string catalogPath) {
try {
var txt = File.ReadAllText(catalogPath);
// 构造最新资源的Catalog的实例
var h = JsonUtility.FromJson<ContentCatalogData>(txt);
var locator = h.CreateLocator();
return locator;
} catch (Exception e) {
Debug.LogError(e.Message);
return null;
}
}
private long CalcDownLoadSize() {
var locator = GetCatalogMap(pathToRemoteCatalogFile);
if (locator == null) {
return 0;
}
long size = 0;
foreach (var kv in locator.Locations) {
var key = kv.Key as string;
if (key == null || !key.EndsWith(".bundle")) {
continue;
}
foreach (var location in kv.Value) {
var sizeData = location.Data as ILocationSizeData;
if (sizeData != null) {
// 与本地的ResourceManager对比下载量
size += sizeData.ComputeSize(location, Addressables.ResourceManager);
}
}
}
return size;
}
Addressables.ResourceManager.InternalIdTransformFunc
private string Init() {
Addressables.ResourceManager.InternalIdTransformFunc = TransFunc;
}
private string TransFunc(IResourceLocation location) {
var path = location.InternalId;
if (location.Data is AssetBundleRequestOptions) {
// 尝试读取本地资源
...
return path;
}
// 没有读取到本地资源,返回原始地址
return location.InternalId;
}
// 手动载入catalog
public async Task<bool> InitCatalogAsync() {
var catalogPath = GetLocalCatalogPath();
foreach (var locator in Addressables.ResourceLocators) {
// 如果已经装载,不用再重新载入
if (locator.LocatorId == catalogPath) {
return true;
}
}
if (mLoadOp.IsValid()) {
return true;
}
var loadOp = Addressables.LoadContentCatalogAsync(catalogPath, false);
mLoadOp = loadOp;
await loadOp.Task;
if (loadOp.Status != AsyncOperationStatus.Succeeded) {
return false;
}
return true;
}
// 要更新手动载入的catalog,必须先卸载已经载入的catalog
public void UnloadCatalog() {
if (mLoadOp.IsDone && mLoadOp.IsValid()) {
Addressables.Release(mLoadOp);
}
}
分组不变,只能在已经存在的分组里增删资源,如果每次都新建分组,即使同名并且资源相同 那么仍然会导致每次打包出的资源guid变化,因为AA分组在每次新建时是取了一个新的分组guid, 后面打包的资源的会依据这个guid做一次hash得到资源guid。
schema不变,每个分组的schema只能用已经存在的,如果schema改变,那么guid也会改变。 并且如果直接使用 AddressableAssetGroup.RemoveSchema的话,会造成schema的资源被删除。 所以如果要清除schema的话,AddressableAssetGroup.Schemas.Clear,这样可以保证不会删除资源。
要将生成好的分组,schema都提交到版本管理,这样每次切到某个版本做资源构建的时候才 不会发生guid变化,