Addressable是Unity推出的打ab包方案,自动依赖;
不需要手动写AB打包方案,不需要关心依赖;
提供本地远程服务异步加载;
打包粒度可调节;
1.Group
Addressable打包需要先将资源放入Group中,按group来打包,每个group对应一个ScriptableObject配置可调节;
Group可以调节包体是本地加载还是远程服务器加载;
可调节压缩方式,是否缓存,请求超时时间等;
打包粒度:
同一Group可选择一起打包,完全分开打包和按标签打包;
同一标签会打成一个ab包,一个资源可选择多个标签;
这里设置Group中的打包粒度;
2.Build
直接将资源拖进对应的分组;
或在资源Inspector界面勾选addressable,资源会自动根据路径生成一个Name(用于加载);
打包资源点击Build-NewBuild-Default Build;
Update是再打过包的基础上,更新资源,热更新时候用到;
Profiles界面设置本地远程加载资源的路径
Addressable提供了本地服务器hosting界面,设置好端口点enable即可,或者使用其他服务器测试也可;
3.Load
3.1加载方式
根据Name加载
Addressables加载需要上面Group中资源的Name=>参数path;
LoadAssetAsync加载完成后返回handle,可以用于释放资源;
因为异步加载资源无法及时返回加载后的资源,可以使用委托加载完成后,接收或操作资源;
public static AsyncOperationHandle<GameObject> LoadPrefab(string path, Action<GameObject> onComplete, bool isInstantiate = true) { var handle = Addressables.LoadAssetAsync<GameObject>(path); handle.Completed += (obj) => { GameObject go = obj.Result; if (isInstantiate) { go = GameObject.Instantiate(go); go.name = go.name.Substring(0, go.name.Length - 7); } onComplete(go); }; return handle; } //提供直接实例化函数 Addressables.InstantiateAsync("Assets/Prefabs/Cube.prefab").Completed += (obj) => { // 已实例化的物体 GameObject cubeObj = obj.Result; };
可以使用async,await方式加载资源,可以直接接收结果;C#async&await&Task
但是webgl可能无法调用,js不支持多线程;
private async void InstantiateCube() { GameObject prefabObj = await Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Cube.prefab").Task; GameObject cubeObj = Instantiate(prefabObj); // InstantiateAsync方法 // GameObject cubeObj = await Addressables.InstantiateAsync("Assets/Prefabs/Cube.prefab").Task; }
根据AssetReference加载
在Inspector界面,将需要加载的文件拖动赋值给AssetReference;
public AssetReference spherePrefabRef; void Start() { spherePrefabRef.LoadAssetAsync<GameObject>().Completed += (obj) => { GameObject spherePrefab = obj.Result; GameObject sphereObj = Instantiate(spherePrefab); }; } //通过标签引用,可以批量加载同标签所有资源 AssetLabelReference label; Addressables.LoadAssetsAsync<Texture2D>(textureLabel, (t) => { //每加载完一次会回调一次 Logger.Log(t.name); });
3.2加载类型
上面Prefab加载已经写了;下面来加载图片,音频和场景;
//图片 Addressables.LoadAssetAsync<Texture2D>("Assets/Textures/a.jpg").Completed += (obj) => { Texture2D tex2D = obj.Result; img.texture = tex2D; img.GetComponent<RectTransform>().sizeDelta = new Vector2(tex2D.width, tex2D.height); }; //textrue2D转sprite Texture2D tex; Sprite sp = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.zero); //场景 var handle = Addressables.LoadSceneAsync("Assets/Scenes/GameScene.unity"); if (handle.Status == AsyncOperationStatus.Failed) { Debug.LogError("ERROR: " + handle.OperationException.ToString()); yield break; } //加载百分比 while (!handle.IsDone) { float percentage = handle.PercentComplete; yield return null; } //音频 Addressables.LoadAssetAsync<AudioClip>("Assets/Scenes/1.ogg");
3.3资源释放
加载的资源销毁组件后并不会销毁资源;(prefab不用如此释放)
直接释放资源,或者释放handle都可;
Addressables.Release(tex2D); Addressables.Release(handle);
3.4PlayMode:
第一个不使用AB加载,开发时候用;
第二个模拟ab加载;
第三个会根据LoadPath去加载真实的AB包读取资源,必须先Build;
4.热更新
Addressables更新流程:
CheckForCatalogUpdates
->检测更新;
UpdateCatalogs
-> 下载Catalogs文件;
GetDownloadSizeAsync
->获取更新资源大小;
DownloadDependenciesAsync
->下载更新资源;
中途断网强退,使用AsyncOperationHandle
的Status判断;
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.UI; public class CheckUpdateAndDownload : MonoBehaviour { public Text updateText; void Start() { StartCoroutine(UpdateAddressable()); } private IEnumerator UpdateAddressable() { AsyncOperationHandle<IResourceLocator> initHandle = Addressables.InitializeAsync(); yield return initHandle; // 检测更新 var checkHandle = Addressables.CheckForCatalogUpdates(true); yield return checkHandle; if (checkHandle.Status != AsyncOperationStatus.Succeeded) { Debug.LogError("CheckForCatalogUpdates Error/n" + checkHandle.OperationException.ToString()); yield break; } if (checkHandle.Result.Count > 0) { var updateHandle = Addressables.UpdateCatalogs(checkHandle.Result, true); yield return updateHandle; if (updateHandle.Status != AsyncOperationStatus.Succeeded) { Debug.LogError("UpdateCatalogs Error/n" + updateHandle.OperationException.ToString()); yield break; } // 更新列表迭代器 List<IResourceLocator> locators = updateHandle.Result; foreach (var locator in locators) { List<object> keys = new List<object>(); keys.AddRange(locator.Keys); // 获取待下载的文件总大小 var sizeHandle = Addressables.GetDownloadSizeAsync(keys.GetEnumerator()); yield return sizeHandle; if (sizeHandle.Status != AsyncOperationStatus.Succeeded) { Debug.LogError("GetDownloadSizeAsync Error/n" + sizeHandle.OperationException.ToString()); yield break; } long totalDownloadSize = sizeHandle.Result; updateText.text = updateText.text + "/ndownload size : " + totalDownloadSize; Debug.Log("download size : " + totalDownloadSize); if (totalDownloadSize > 0) { // 下载 var downloadHandle = Addressables.DownloadDependenciesAsync(keys, true); while (!downloadHandle.IsDone) { if (downloadHandle.Status == AsyncOperationStatus.Failed) { Debug.LogError("DownloadDependenciesAsync Error/n" + downloadHandle.OperationException.ToString()); yield break; } // 下载进度 float percentage = downloadHandle.PercentComplete; Debug.Log($"已下载: {percentage}"); updateText.text = updateText.text + $"/n已下载: {percentage}"; yield return null; } if (downloadHandle.Status == AsyncOperationStatus.Succeeded) { Debug.Log("下载完毕!"); updateText.text = updateText.text + "/n下载完毕"; } } } } else { updateText.text = updateText.text + "/n没有检测到更新"; } } }