Group设置中可以配置资源的更新模式,有两个配置决定资源的更新模式:LoadPath、UpdateRestriction。
LoadPath:
LoadPath有local和remote之分,remote是指远程地址,比如http请求,而local则是本地加载。AAS对LoadPath选项的设计初衷就是,需要更新的资源就用remote方式,不需要更新的资源使用local方式。
UpdateRestriction:
UpdateRestriction也有两种选项:Can Change Post Release、Can not Change Post Release,即发布后可更改(NonStatic)、发布后不可更改(Static)。
比如GroupA中有资源A发生了变化,需要进行更新:
若GroupA设置为NonStatic,则代表将资源A下载下来后Group结构不作任何变化,但会下载新GroupA的bundle,即可理解为直接替换资源,相当于全量更新(前提是Group是Pack Together方式)。
而若GroupA设置为Static则代表原Group的资源不可修改,在下载A资源的同时将更新Catalog中GroupA的结构,将A资源移出GroupA,放入新增的Group组中(新增ContentUpdate组,需配合AAS工具栏的Check for Content Update Restriction工具使用),只会下载新增的Group:ContentUpdate(包含资源A和其依赖项),相当于增量更新。
新增的ContentUpdate组:
综上,一共有4种模式组合可供使用,即local-static,local-nonstatic,remote-static,remote-nonstatic。其中local-nostatic没有意义,排除这种组合。其实local模式其实设计上是不允许更新的,但是AAS还是做了更新兼容,于是local-static配合Check for Content Update Restriction也是可以增量更新的。
我们列出local-static、remote-static、remote-nonstatic的效果:
-
local-static:打包时带入开发包中。更新时走增量更新。
-
remote-static:打包时放在服务器,不带入开发包。更新时走增量更新。
-
remote-nonstatic:打包时放在服务器,不带入开发包。更新时走全量更新。
由于static的增量更新会修改Group的结构,若采用static方式,考虑一种极限状态,即所有的资源都进行过更新,那么最后整个项目的资源Group应该会全部变成ContentUpdate组:ContentUpdate、ContentUpdate1、ContentUpdate2…,这样的话我们的分组策略就完全被打乱了,所以第一时间排除static模式。
所以,我们剩下的方案就只有remote-nonstatic模式。但是我们期望的更新方式是打包时能带入开发包,而不是边玩边下。事实上,可以用AAS的InternalIdTransformFunc函数定制出这样的机制:打包时,强行把remote打出的catalog和资源文件放入包体,资源加载时,优先判断使用本地包体中的资源。
AAS提供了InternalIdTransformFunc函数,来帮助开发者自定义修改运行时的资源索引地址,比如请求本地资源A的地址InternalIdTransformFunc返回值为“Assets/a.prefab”,在该函数中可修改为返回“Assets/b.prefab”以重定向使用b资源。
官方示例代码如下:
void Start()
{
Addressables.ResourceManager.InternalIdTransformFunc = TransformFunc;
}
string TransformFunc(IResourceLocation location)
{
//Implement a method that gets the base url for a given location
string baseUrl = GetBaseURL(location);
//Get the url you want to use to point to your current server
string currentUrlToUse = GetCurrentURL();
return location.InternalId.Replace(baseUrl, currentUrlToUse);
}
因此,我们可以设置Group的Bundle Naming Mode配置项为Use Hash of AssetBundle,配合InternalIdTransformFunc函数实现优先使用本地资源的设计。主要思路为:拦截资源地址请求,替换成开发包体内本地资源地址(随包资源地址,非缓存地址),再判断本地是否有该最新资源,有的话就直接返回本地资源地址。而判断资源是否为最新资源,则只需要资源构建时使用Use Hash of AssetBundle(为了避免暴露资源名称,我们不使用Append Hash to Filename)配置对资源以hash值命名即可,这样只要路径存在,资源就是最新的。
实现代码如下:
private static string InternalIdTransformFunc(UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation location)
{
//判定是否是一个AB包的请求(真机运行和exist building模式走这里)
if (location.Data is AssetBundleRequestOptions)
{
#if UNITY_EDITOR
var path = string.Format("{0}/{1}/{2}/{3}", Application.dataPath.Replace("/Assets", ""), Addressables.RuntimePath, UnityEditor.EditorUserBuildSettings.activeBuildTarget, location.PrimaryKey);
#elif UNITY_ANDROID
var path = string.Format("{0}/Android/{1}", Addressables.RuntimePath, location.PrimaryKey);
#else
var path = string.Format("{0}/iOS/{1}", Addressables.RuntimePath, location.PrimaryKey);
#endif
if (FileUtils.IsFileExists(path))
{
return path;
}
}
return location.InternalId;
}
使用non-static更新模式,若Group使用Pack Together打包模式,则更新时会更新整个Group的资源,而使用Pack Separate打包模式,则加载时由于bundle过多,会降低加载速度。因此,该方式对Group的颗粒度划分有更高的要求。相关内容可见《六、Group控制AB包颗粒度策略》。