您现在的位置是:首页 >技术交流 >Unity Addressables热更流程网站首页技术交流

Unity Addressables热更流程

枫十七 2024-07-14 12:01:02
简介Unity Addressables热更流程

一、分组(网上教程一大堆)

二、构建

        构建前设置:

                1、分组设置。所有组做远端构建加载选择,RemoteBuildPath 。RemoteLoadPath

                 

                2、AddressableAssetSettings设置

         3、构建

                

 三、导出信息分析:

        1、Assets同级目录下,ServerData内,包含所有所需文件。

        2、对应平台下。catalog.hash和catalog.json为版本检测和资源记录文件。其他为AB包。

        3、Assets/AddressableAssetsData/Android下为热更记录文件,打热更包所需。

四、打热更包:

        

        选择上述三、3的文件。更新后ServerData下catalog和修改的AB发生变化。

五、地址重定向:

Addressables.ResourceManager.InternalIdTransformFunc = LoadFunc;

public static bool CustomNet { get; private set; }
    public static string VersionURL;
    public static string AddressURL;
    private static string OldUrl = "http://localhost/";
    /// <summary>
    /// 设置热更地址
    /// </summary>
    /// <param name="_versionUrl">版本地址</param>
    /// <param name="_addressUrl">资源地址</param>
    public static void SetCustomAddress(string _versionUrl, string _addressUrl)
    {
        CustomNet = true;
        VersionURL = _versionUrl;
        AddressURL = _addressUrl;
#if UNITY_EDITOR
        UnityEngine.Debug.Log("设置版本地址:" + VersionURL);
        UnityEngine.Debug.Log("设置资源地址地址:" + AddressURL);
#endif
        Addressables.ResourceManager.InternalIdTransformFunc = LoadFunc;
    }

    private static string LoadFunc(IResourceLocation location)
    {
        if (location.ResourceType == typeof(IAssetBundleResource) && location.InternalId.StartsWith("http"))
        {
            string abAddress = location.InternalId.Replace(OldUrl, AddressURL);
            return abAddress;   
        }
        return location.InternalId;
    }

 六、热更,创建服务器,把ServerData/Android放上去。即可热更。

        1、热更下载的catalog文件在 Application.persistentDataPath

        2、下载的AB资源在 :C:UsersAdministratorAppDataLocalLowUnity

七、热更检测代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.Networking;

public class Updater : MonoBehaviour
{
    public enum HotState
    {
        None,
        Version,
        Hash,
        Json,
        Download,
        StopDownload,
    }
    private HotState m_HotState = HotState.None;
    [SerializeField]
    public bool Hot = true;
    //多平台版本检测 -> 地址 
    [SerializeField]
    public string VersionURL = "CDN地址";
    [SerializeField]
    public string Catalog = "catalog";
    // 拉取CDN地址错误
    public System.Action<string> FirstAddressError;
    // 拉取hash版本地址错误
    public System.Action<string> VerionCheckError;
    // 拉取json版本地址错误
    public System.Action<string> VerionUpdateError;
    // 得到下载大小回调
    public System.Action<long> ResourceSize;
    // 下载进度回调
    public System.Action<float> DowonloadPercent;
    // 断网回调
    public System.Action DisconnectNet;
    // 重新联网回调
    public System.Action ReconnectNet;
    public static Updater Instance;
    private readonly List<object> _updateKeys = new List<object>();
    private IEnumerator IDownLoad;
    private NetworkReachability LastNet;
    private void Awake()
    {
        LastNet = Application.internetReachability;
        Instance = this;
#if UNITY_EDITOR
        if (!Hot) // 动态设置分组模式
        {
        }
#endif
    }
    private void Start()
    {
        if (!Hot)
            EnterGame();
        else
            StartCoroutine(SetAddress());
    }
    private IEnumerator SetAddress()
    {
        m_HotState = HotState.Version;
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            DisconnectNet?.Invoke();
            yield break;
        }
        UnityWebRequest request = UnityWebRequest.Post(VersionURL, "");
        yield return request.SendWebRequest();
        if (request.isHttpError || request.error != null)
        {
            FirstAddressError?.Invoke(request.error);
            Debug.LogError("配置地址出错:" + request.error);
            yield break;
        }
        else
        {
            string datas = request.downloadHandler.text;
            CDNAddress CDN = JsonUtility.FromJson<CDNAddress>(datas);
            string url = CDN.GetGetAddressURL();
            Debug.Log("CDN :" + url);
            url = "http://192.168.11.51:8089/";
            AddressablesExt.SetCustomAddress(url + Catalog + ".hash", url);
        }
        StartCoroutine(DoUpdateAddressadble());
    }
    IEnumerator DoUpdateAddressadble()
    {
        m_HotState = HotState.Hash;
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            DisconnectNet?.Invoke();
            yield break;
        }
        AsyncOperationHandle<IResourceLocator> initHandle = Addressables.InitializeAsync();
        yield return initHandle;
        //检测更新
        AsyncOperationHandle<List<string>> checkHandle = Addressables.CheckForCatalogUpdates(false);
        yield return checkHandle;
        if (checkHandle.Status != AsyncOperationStatus.Succeeded)
        {
            VerionCheckError?.Invoke(checkHandle.OperationException.ToString());
            Debug.LogError("版本检测失败:" + checkHandle.OperationException.ToString());
            yield break;
        }
        if (checkHandle.Result.Count > 0)
        {
            m_HotState = HotState.Json;
            AsyncOperationHandle<List<IResourceLocator>> updateHandle = Addressables.UpdateCatalogs(checkHandle.Result, false);
            yield return updateHandle;
            if (updateHandle.Status != AsyncOperationStatus.Succeeded)
            {
                Debug.LogError("版本更新失败:" + updateHandle.OperationException.ToString());
                VerionUpdateError?.Invoke(updateHandle.OperationException.ToString());
                yield break;
            }
            // 更新列表迭代器
            List<IResourceLocator> locators = updateHandle.Result;
            foreach (var locator in locators)
            {
                _updateKeys.AddRange(locator.Keys);
                // key 里一堆乱七八糟的东西  
            }
            Addressables.Release(checkHandle);
            Addressables.Release(updateHandle);
        }
        else //版本已经更新过的,采用这种方式
        {
            IEnumerable<IResourceLocator> locators = Addressables.ResourceLocators;
            foreach (var locator in locators)
            {
                _updateKeys.AddRange(locator.Keys);
                // key 里一堆乱七八糟的东西  
            }
        }
        AsyncOperationHandle<long> sizeHandle = Addressables.GetDownloadSizeAsync(_updateKeys as IEnumerable<object>);
        yield return sizeHandle;
        Debug.Log("热更资源总大小:" + NetFormat.GetDisplaySize(sizeHandle.Result));
        if (sizeHandle.Result > 0)
        {
            ResourceSize?.Invoke(sizeHandle.Result);
            StartDownLoad();
        }
        else
        {
            Debug.Log("没有检测到更新 - 无需下载");
            EnterGame();
        }
        Addressables.Release(sizeHandle);
    }
    public void StartDownLoad()
    {
        IDownLoad = DownLoad();
        StartCoroutine(IDownLoad);
    }
    private void StopDownLoad()
    {
        m_HotState = HotState.StopDownload;
        StopCoroutine(IDownLoad);
    }
    private void Update()
    {
        if (Application.internetReachability != LastNet)
        {
            switch (Application.internetReachability)
            {
                case NetworkReachability.NotReachable:
                    if (m_HotState == HotState.Download)
                    {
                        StopDownLoad();
                    }
                    DisconnectNet?.Invoke();
                    Debug.LogError("网络链接未打开");
                    break;
                case NetworkReachability.ReachableViaCarrierDataNetwork:
                case NetworkReachability.ReachableViaLocalAreaNetwork:
                    ReconnectNet?.Invoke();
                    TryAgin();
                    Debug.Log("流量 OR  WIFI");
                    break;
                default:
                    break;
            }
            LastNet = Application.internetReachability;
        }
    }
    public void TryAgin()
    {
        Debug.Log(m_HotState);
        switch (m_HotState)
        {
            case HotState.None:
                break;
            case HotState.Version:
                StartCoroutine(SetAddress());
                break;
            case HotState.Hash:
            case HotState.Json:
                StartCoroutine(DoUpdateAddressadble());
                break;
            case HotState.Download:
                StartDownLoad();
                break;
            case HotState.StopDownload:
                StartDownLoad();
                break;
            default:
                break;
        }
    }
    public HotState GetHotState()
    {
        return m_HotState;
    }
    public void Quit()
    {
        Application.Quit();
    }
#if UNITY_EDITOR
    [UnityEditor.MenuItem("AATool/PrintState")]
    private static void PrnitState()
    {
        Debug.LogError(Instance.m_HotState);
    }
#endif
    private IEnumerator DownLoad()
    {
        m_HotState = HotState.Download;
        if (Application.internetReachability == NetworkReachability.NotReachable)
        {
            DisconnectNet?.Invoke();
            yield break;
        }
        AsyncOperationHandle downHandle = Addressables.DownloadDependenciesAsync(_updateKeys as IEnumerable<object>, Addressables.MergeMode.Union, false);
        while (!downHandle.IsDone)
        {
            float _bar = downHandle.GetDownloadStatus().Percent;
            DowonloadPercent?.Invoke(downHandle.GetDownloadStatus().Percent);
            UnityEngine.Debug.Log(_bar);
            yield return null;
        }
        yield return downHandle;
        if (downHandle.Status != AsyncOperationStatus.Succeeded)
        {
            Debug.LogError("下载失败 - 重新尝试下载");
            StartDownLoad();
            Addressables.Release(downHandle);
            yield break;
        }
        else
        {
            Addressables.Release(downHandle);
            // 进入游戏
            EnterGame();
        }
    }
    // 进入游戏
    void EnterGame()
    {
        // TODO
        Debug.Log("进入游戏");
        RootTools.MainScript.GetOrAddComponent<GameRoot>();
        Object.Destroy(gameObject);
    }
    private void OnApplicationQuit()
    {

    }
    private void OnApplicationFocus(bool focus)
    {

    }
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。