说明:下文中Addressable简称Aa
使用Aa的原因
- 使用Aa可以做到动态加载,这里的动态加载指的是当我们需要某个资源的时候才去加载它。这里的资源可以是预制体、图片、音效等等。
这样做的好处是: 避免资源全部一次性加载,占用不必要的内存,避免手机资源加载缓慢、手机发热,卡顿。 - 可以做到资源的热更,注意这里的资源不包括代码。这部分后面再细说。
安装Aa
.CN后缀的是加密版本,但是好像有看到文章说有大量GC存在,还没查验过。
这里先使用没有后缀版本的。
使用Aa
1. 创建Aa
2. 添加需要动态加载的资源
方法1: 选中资源,在Inspector中勾选Aa
方法2: 选中资源,直接拖拽到Aa面板中
注意事项
添加了需要动态加载的资源之后,可以右键简化名称,这里的名称是下面代码中所需要的参数。
3. 使用代码加载
方法一:
//使用LoadAssetAsync方法
Addressables.LoadAssetAsync<GameObject>("Alpha@Alpha").Completed += (handle) =>
{
// 预设物体
GameObject result = handle.Result;
// 实例化
GameObject alpha = Instantiate(result);
alpha.transform.position = new Vector3(-4, 0, 0);
};
//使用InstantiateAsync方法
Addressables.InstantiateAsync("Alpha@Alpha").Completed += (handle) =>
{
// 已实例化的物体
GameObject alpha = handle.Result;
alpha.transform.position = new Vector3(-2, 0, 0);
};
LoadAssetAsync和InstantiateAsync 都可以使用加载Aa资源。两者的区别是,InstantiateAsync直接帮你实例化了,省去了LoadAssetAsync中的Instantiate过程。
方法二
使用async、await的方法
private async void InstantiateBetaObj()
{
//使用LoadAssetAsync方法
GameObject betaPrefabObj = await Addressables.LoadAssetAsync<GameObject>("Beta@Beta").Task;
GameObject betaObj1 = Instantiate(betaPrefabObj);
betaObj1.transform.position = new Vector3(2, 0, 0);
//使用InstantiateAsync方法
GameObject betaObj2 = await Addressables.InstantiateAsync("Beta@Beta").Task;
betaObj2.transform.position = new Vector3(4, 0, 0);
}
注意: 测试的时候需要加上函数头中的async,别只复制了函数体内的内容。
方法三
声明AssetReference类型,AssetReference类型是弱引用类型,是不会打进包体中。如果声明GameObject类型就会打进包体中。
//先声明类型
public AssetReference newPrefabRef;
newPrefabRef.LoadAssetAsync<GameObject>().Completed += (obj) =>
{
// 预设
GameObject result = obj.Result;
// 实例化
GameObject newPreObj = Instantiate(result);
newPreObj.transform.position = new Vector3(0.9f, 0, 0);
};
newPrefabRef.InstantiateAsync().Completed += (obj) =>
{
// 已实例化的物体
GameObject newPreObj = obj.Result;
newPreObj.transform.position = new Vector3(-0.9f, 0, 0);
};
这里我们是使用声明的类型的LoadAssetAsync ,InstantiateAsync 的来创建的。
至此,Aa的简单使用过程就介绍完了。下面介绍其他内容。
Aa的加载方式
Use Asset Database: 不需要 Build 打Addressable资源包就可以使用代码加载(开发中选择这个就可以了)。
Simulate Groups (advanced): 不需要 Build 打Addressable资源包就可以使用代码加载。 但是可以模拟Aa的加载,并统计分析数据,方便我们进行快速分析(详细步骤在下面)。
Use Exising Build: 需要先执行Build打出Addressable资源包,它会根据Load Path去加载真正的AssetBundle文件并读取资源。如果不先Build,运行时会报错。同样也可以使用分析器看到资源的依赖,引用计数。
查看统计引用
- 勾选AddressableAssetSetting中的Send Profiler Events
- 打开窗口
构建包体外的Aa
我们也可以构建在包体外的Aa资源,通过网络加载资源包。
创建分组
将创建出来的分组选中右键之后,可以改名。我这里便是改成RemoteAssets。
指定包外Aa的BuildPath和LoadPath
找到如下图中路径,选中与上一步创建的同名配置文件,在Inspector中修改BuildPaht,LoadPath为RemoteBuildPath,RemoteLoadPath。
(路径中的AddressableAssetsData是安装Aa之后就会创建的)
修改BuildPath和LoadPaht的具体路径
注意这里与上面内容的区别。上面的内容是指定Aa的路径是本地还是远程。
而这里的内容是修改本地或远程的具体路径。打开方式如下所示:
这里我们就使用真实的环境测试了,而不是用Addressable提供的Hosting。
到这里的时候,我们尝试Build下
构建成功之后可以看到项目路径下多了个ServerData文件夹,里面的内容就是我们创建的包体外的资源。
开始测试
现在让我们来测试一下!
首先我们需要把Play Mode Script 修改为 Use Existing Build 模式。
这里改为UseExisting Build 模式是为了模拟真实的环境,如果选为另外两个模式,我们都不需要把资源放到服务器上就可以运行成功了,所以测试是不准确的。
当然我们这里只是为了测试下通过网络加载是否能加载到我们需要的Aa资源。实际开发中不需要修改为Use Exiting Build模式。
代码部分
无论这个Aa资源是在包外的还是在包内的,加载方式都是一样的。
- 我们创建个空的RawImage对象
- 引用RawImage对象
public RawImage headIcon;
- 加载图片,赋值给RawImage
Addressables.LoadAssetAsync<Texture2D>("pansen").Completed += (obj) =>
{
// 图片
Texture2D tex2D = obj.Result;
headIcon.texture = tex2D;
headIcon.GetComponent<RectTransform>().sizeDelta = new Vector2(tex2D.width, tex2D.height);
};
将我们之前创建的Aa资源放到服务器
将我们上面创建的ServerData/StandaloneWindows64下的资源放到服务器上。这里就不演示了。
开始运行
点击play之后,就可以看到效果了。
测试
现在我想知道,这个资源是否就是从网络上下载的呢?
- 我们先把服务器上的资源删除。
- 删除缓存在本地的资源。使用Everything工具搜索Aa资源的hash值,注意是搜索hash值,不要搜索整个Aa资源的名字,那样会搜索不到缓存在我们电脑上的资源。
- 把构建在本地的Aa资源删除,也就是ServerData/StandaloneWindows64 下的资源
现在我们再运行下试试。发现会报如下错误:
颗粒细度修改
我们Build的时候默认按Group为颗粒进行打包,如果我们想修改成以单个文件为颗粒改怎么做?
我们先在RemoteAssets中再添加一张图片,然后Build下。
可以看到只有一个.bundle文件。
以单个文件为颗粒细度
选择远端的配置文件,在Inspector中修改Bundle Mode为Pack Separately即可
我们再尝试Build下:
可以看到输出了两个.bundle。分别对应每个图片资源。
以Label标签为颗粒细度
上面的颗粒细度太细了,有时候我们不想分的这么细。此时我们可以使用以Label为颗粒细度。
新建标签
- 打开窗口
- 添加标签。按加号即可添加,这里我添加了两个:Audio,Texture。
设置标签
我们给RemoteAssets添加一个声音资源,并给所有的资源设置标签。如下所示:
修改打包方式
选中我们的远端资源配置文件,在Inspector中修改Bundle Mode为Pack Together By Label即可
结果
Build之后,可以看到以Label为细度的.bundle。
一个资源多个Label标签
我们也可以给一个资源打上多个标签,如下:
Build之后的结果如下:
注意:上面结果中,中间这个.bundle里面只有"pansen"这张图片,最后的texture名的.bundle里面只有“gouxiong”这张图片。也就是说,虽然我们给两张图片都设置了“Textrue”这个Label,但是构建的时候不会包含对方。
使用代码加载同个Label标签资源
虽然上面打包的时候,含有texture标签两个资源被分别打包了。但是我们这里获取到的是只要含有同个标签的都会被加载到。
- 声明AssetLabelReference类型
public AssetLabelReference textureLabel;
-
设置为Texture标签
-
代码获取
//加载同个Label标签资源
Addressables.LoadAssetsAsync<Texture2D>(textureLabel, (texture) =>
{
Debug.Log("加载资源: " + texture.name);
});
- 结果
pansen和gouxiong 都输出了。
热更新资源
现在我需要把下图中框中的头像替换一个
换成下面这个:
开启Build Remote Catalog
如下所示,选择AddressableAssetSettings,勾选Build Remote Catalog。选择BuildPath和LocalPath指定的值。
重新构建
重新Build之后,可以看到输出目录多了以catalog开头的.hash和.json文件。这两个文件是在我们上面勾选了Build Remote Catalog之后才会出现的。
为了测试的时候方便,我这里的RemoteAssets下只有一张"pansen"的图片,所以只有一个.bundle文件。
注意事项
注意,如果需要热更资源,那么确保被替换的资源有catalog的两个文件。也就是说如果我一开始没有勾选Build Remote Catalog,然后Build。这个时候远程动态加载图片是没问题的,但是热更替换图片就不行了,因为我们需要对catalog的两个文件进行比较。接着往下看。
测试资源加载
把上面构建的文件拖到服务器上。然后使用代码测试是否能正常加载。代码还是使用上面的,不再累述:
public RawImage headIcon;
Addressables.LoadAssetAsync<Texture2D>("pansen").Completed += (obj) =>
{
// 图片
Texture2D tex2D = obj.Result;
headIcon.texture = tex2D;
headIcon.GetComponent<RectTransform>().sizeDelta = new Vector2(tex2D.width, tex2D.height);
};
结果如下,到这里说明我们加载成功了:
替换资源
把之前使用的"pansen"资源替换成下面的
构建: Update a Previous Build
注意这里不是我们上面经常使用的 Build – New Build – Defaule Build Script了,而是使用下面这个:
点击之后会弹出一个文件框,让你选择.bin文件。因为我是在电脑上测试,所以选择window。
Step1:
Step2:
双击选中.bin文件之后。会在BuildPaht中出现新的.bundle,以及被修改的catalog的两个文件。
测试
注意这里需要把被修改的catalog的两个文件,以及新增的.bundle文件上传。只上传.bundle是不会生效的。
重新运行效果如下:
如果你的结果没有变化,可能是本地文件缓存的原因。根据上文使用Everything查找缓存的方法,删除之前的缓存。同时也把ServerData/StandaloneWindow下之前的.bundle删除。
Aa资源释放
加载代码:
Addressables.LoadAssetAsync<Texture2D>("pansen").Completed += (obj) =>
{
// 图片
tex2D = obj.Result;
headIcon.texture = tex2D;
headIcon.GetComponent<RectTransform>().sizeDelta = new Vector2(tex2D.width, tex2D.height);
};
释放代码:
注意需要使用Addressables.Release。
public void ReleaseAaAsset()
{
//释放资源
if (tex2D != null)
{
Addressables.Release(tex2D);
}
//销毁RawImage
Destroy(headIcon.gameObject);
}
可以打开EventViewr查看变化:
释放前:
释放后:
释放后已经没有了"pansen"的资源了。
文章内容参考:link