Unity-Adressable打包熱更
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沒有檢測到更新";
}
}
}