Unity-Adressable打包熱更

Addressable是Unity推出的打ab包方案,自動依賴;

不需要手動寫AB打包方案,不需要關心依賴;

提供本地遠程服務異步加載;

打包粒度可調節;

1.Group

Addressable打包需要先將資源放入Group中,按group來打包,每個group對應一個ScriptableObject配置可調節;

Group可以調節包體是本地加載還是遠程服務器加載;

可調節壓縮方式,是否緩存,請求超時時間等;

image-20220515195301242

打包粒度:

同一Group可選擇一起打包,完全分開打包和按標籤打包;

同一標籤會打成一個ab包,一個資源可選擇多個標籤;

這裡設置Group中的打包粒度;

image-20220515195444849

2.Build

直接將資源拖進對應的分組;

image-20220515200525999

或在資源Inspector界面勾選addressable,資源會自動根據路徑生成一個Name(用於加載);

image-20220515200446115

打包資源點擊Build-NewBuild-Default Build;

Update是再打過包的基礎上,更新資源,熱更新時候用到;

image-20220515200032183

Profiles界面設置本地遠程加載資源的路徑

image-20220515200637866

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;

image-20220515195628905

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沒有檢測到更新";
        }
    }
}
Tags: