Unity 使用Addressable加载远程资源

本文介绍了如何使用Unity的AddressableAssets系统进行资源热更新,包括设置远程加载路径、打包资源、上传到对象存储服务,以及创建下载进度条来跟踪加载进度。同时,文章提供了ResourceManager类的代码示例,用于管理资源和场景的加载,并展示了加载回调的使用方法。

1. 如需资源热更可以勾选此选项

2. 将需要加载的资源拖到Group下并修改Group的加载方式

2.1 将该Group的加载方式改为远程

3. 配置存储桶并修改Addressable的远程加载路径

以腾讯云为例各家对象存储页面可能有点差异,请自行查阅相关文档。

3.1 将访问权限改为公有读私有写

3.2 复制访问域名并修改Addressable的加载路径

 [BuildTarget] 对应当前的平台

4. 完成上述操作后即可打包部署到对象存储桶

4.1 打包bundle

打包路径在AddressableAssetSettings可以查看,默认路径在项目的根目录

4.2 上传至对象存储桶(将打包好的文件夹整体上传)

5. 加载资源

5.1 制作下载进度条并将 DownloadProgressPanel 脚本挂到其父物体

 

using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class DownloadProgressPanel : MonoBehaviour
{
    public Image progressBarImage;
    public TMP_Text progressTMP;
    public TMP_Text downloadSizeTMP;

    private void OnEnable()
    {
        progressBarImage.fillAmount = 0f;
        progressTMP.text = "0%";
        downloadSizeTMP.text = "资源下载中(0MB/0MB)";
    }
}

 5.2 创建空物体并将 ResourceManager 挂到该物体上

Mono单例

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;
    public static T Instancea
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();
                if (_instance == null)
                {
                    GameObject singletonObject = new GameObject();
                    _instance = singletonObject.AddComponent<T>();
                    singletonObject.name = typeof(T).ToString() + " (Singleton)";
                    DontDestroyOnLoad(singletonObject);
                }
            }
            return _instance;
        }
    }

    protected virtual void Awake()
    {
        if (_instance == null)
        {
            _instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}
using System;
using System.Collections;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug;

public class ResourceManager : MonoSingleton<ResourceManager>
{
    private void OnEnable()
    {
        Addressables.InitializeAsync().Completed += AddressableInitialize;
    }

    private void AddressableInitialize(AsyncOperationHandle<IResourceLocator> assets)
    {
        print("Addressable Initialization Complete");
    }

    /// <summary>
    /// 加载资源
    /// </summary>
    /// <param name="address">资源名称</param>
    /// <param name="onStartCallback">开始加载回调</param>
    /// <param name="onProgressTracker">进度回调</param>
    /// <param name="onComplete">完成加载回调</param>
    /// <typeparam name="T"></typeparam>
    public void LoadAsset<T>(string address,Action onStartCallback=null,Action<ProgressTracker> onProgressTracker=null,Action<T> onComplete=null) where T : UnityEngine.Object
    {
        StartCoroutine(CoroutineLoadAsset(address,onStartCallback,onProgressTracker,onComplete));
    }
    
    IEnumerator CoroutineLoadAsset<T>(string assetLabel,Action onStartCallback=null,Action<ProgressTracker> onProgressTracker=null, Action<T> onCompleted=null) where T : UnityEngine.Object
    {
        // 初始化 Addressable
        yield return Addressables.InitializeAsync();
        onStartCallback?.Invoke();
        var totalSize = 0f;
        // 获取场景的下载大小
        yield return GetDownLoadAssetSize(assetLabel, (size) => { totalSize = size; });
        // 创建进度追踪器
        var progress = new ProgressTracker(totalSize);
        // 下载资源
        var asset= Addressables.LoadAssetAsync<T>(assetLabel);
        if (asset.Status == AsyncOperationStatus.Failed)
        {
            Debug.LogError("Asset Load Error: " + asset.OperationException);
            yield break;
        }
        while (!asset.IsDone)
        {
            // 更新进度追踪器的进度
            progress.UpdateProgress(asset.GetDownloadStatus().Percent, asset.GetDownloadStatus().DownloadedBytes);
            onProgressTracker?.Invoke(progress);
            yield return null;
        }
        onCompleted?.Invoke(asset.Result);
    }


    /// <summary>
    /// 加载场景
    /// </summary>
    /// <param name="sceneLabel">场景名称</param>
    /// <param name="onStartCallback">开始加载回调</param>
    /// <param name="onProgressTracker">进度回调</param>
    /// <param name="onCompleted">完成加载回调</param>
    public void LoadScene(string sceneLabel,Action onStartCallback=null,Action<ProgressTracker> onProgressTracker=null,Action onCompleted=null)
    {
        StartCoroutine(CoroutineLoadScene(sceneLabel,onStartCallback,onProgressTracker,onCompleted));
    }

    
    IEnumerator CoroutineLoadScene(string sceneLabel,Action onStartCallback=null, Action<ProgressTracker> onProgressTracker=null,Action onCompleted = null)
    {
        // 初始化 Addressable
        yield return Addressables.InitializeAsync();
        onStartCallback?.Invoke();
        var totalSize = 0f;
        // 获取场景的下载大小
        yield return GetDownLoadAssetSize(sceneLabel, (size) => { totalSize = size; });
        // 创建进度追踪器
        var progress = new ProgressTracker(totalSize);
        // 下载场景并设定加载模式为Additive
        var scene=Addressables.LoadSceneAsync(sceneLabel, LoadSceneMode.Additive);
        if (scene.Status == AsyncOperationStatus.Failed)
        {
            Debug.LogError("Scene Load Error: " + scene.OperationException);
            yield break;
        }
        while (!scene.IsDone)
        {
            // 更新进度追踪器的进度
            progress.UpdateProgress(scene.GetDownloadStatus().Percent, scene.GetDownloadStatus().DownloadedBytes);
            onProgressTracker?.Invoke(progress);
            yield return null;
        }
        onCompleted?.Invoke();
    }

    /// <summary>
    /// 获取资源大小
    /// </summary>
    /// <param name="assetLabel"></param>
    /// <param name="callback"></param>
    /// <returns></returns>
    private IEnumerator GetDownLoadAssetSize(string assetLabel,Action<float> callback)
    {
        var downloadSizeHandle = Addressables.GetDownloadSizeAsync(assetLabel);
        yield return downloadSizeHandle;
        if (downloadSizeHandle.Status != AsyncOperationStatus.Succeeded)
        {
            Debug.LogError("Failed to get download size: " + downloadSizeHandle.OperationException);
            yield break;
        }
        // 将下载大小转换为以MB为单位的浮点数
        long downloadSize = downloadSizeHandle.Result;
        float totalSize = (float)downloadSize / (1024 * 1024);
        Debug.Log($"下载的资源大小:{downloadSize}");
        callback?.Invoke(totalSize);
    }
    
    private void OnDisable()
    {
        Addressables.InitializeAsync().Completed -= AddressableInitialize;
    }
}
public class ProgressTracker
{
    private readonly float m_AssetTotalSize;
    private float m_CurrentProgress;
    private float m_CurrentDownloadSize;

    public ProgressTracker(float assetTotalSize)
    {
        this.m_AssetTotalSize = assetTotalSize;
        this.m_CurrentProgress = 0f;
    }

    /// <summary>
    /// 更新下载进度
    /// </summary>
    /// <param name="percent">进度</param>
    /// <param name="downloadedBytes">下载字节数</param>
    public void UpdateProgress(float percent, long downloadedBytes)
    {
        m_CurrentDownloadSize = (float)downloadedBytes / (1024 * 1024);
        m_CurrentProgress = percent;
    }
    
    /// <summary>
    /// 获取下载进度
    /// </summary>
    /// <returns></returns>
    public float GetProgress()
    {
        return m_CurrentProgress;
    }
    
    /// <summary>
    /// 获取当前下载大小
    /// </summary>
    /// <returns></returns>
    public float GetCurrentDownloadSize()
    {
        return m_CurrentDownloadSize;
    }
    
    /// <summary>
    /// 获取资源总大小
    /// </summary>
    /// <returns></returns>
    public float GetAssetTotalSize()
    {
        return m_AssetTotalSize;
    }
    
    
    /// <summary>
    /// 标记完成
    /// </summary>
    public void Complete()
    {
        m_CurrentProgress = m_AssetTotalSize;
    }
}

5.3 加载资源,创建空物体并挂上该脚本 

using UnityEngine;

public class TestLoadObject : MonoBehaviour
{
    [SerializeField]private DownloadProgressPanel progressPanel;
    void Start()
    {
        ResourceManager.Instance.LoadAsset<GameObject>("Set Costume_02 SD Misaki", () =>
        {
            print($"---> Start Loader...");
        },(tracker)=>
        {
            GetCurrentProgress(tracker.GetProgress(), tracker.GetCurrentDownloadSize(), tracker.GetAssetTotalSize());
            print($"---> Update Progress...");
        }, (obj) =>
        {
            progressPanel.gameObject.SetActive(false);
            Instantiate(obj);
            print($"---> Main Scene Load Complete!");
        });
    }
    
    private void GetCurrentProgress(float percentage,float downloadSize,float totalSize)
    {
        progressPanel.progressBarImage.fillAmount = percentage;
        progressPanel.progressTMP.text = $"{(percentage * 100f):f0}%";
        progressPanel.downloadSizeTMP.text = $"资源下载中({downloadSize:f1}MB/{totalSize:f1}MB)";
    }
}

5.4  加载场景,创建空物体并挂上该脚本

using UnityEngine;

public class TestLoadScene : MonoBehaviour
{
    [SerializeField]private DownloadProgressPanel progressPanel;
    void Start()
    {
        ResourceManager.Instance.LoadScene("MainScene", () =>
        {
            progressPanel.gameObject.SetActive(true);
            print($"---> Start Loader...");
        },(tracker)=>
        {
            GetCurrentProgress(tracker.GetProgress(), tracker.GetCurrentDownloadSize(), tracker.GetAssetTotalSize());
            print($"---> Update Progress...");
        }, () =>
        {
            progressPanel.gameObject.SetActive(false);
            print($"---> Main Scene Load Complete!");
        });
    }
    private void GetCurrentProgress(float percentage,float downloadSize,float totalSize)
    {
        progressPanel.progressBarImage.fillAmount = percentage;
        progressPanel.progressTMP.text = $"{(percentage * 100f):f0}%";
        progressPanel.downloadSizeTMP.text = $"资源下载中({downloadSize:f1}MB/{totalSize:f1}MB)";
    }
}

6. 打包运行

加载单个资源

加载场景

下载内容大小提示

7.更新

2023.08.16

  1. 新增进度追踪器脚本
  2. 新增下载内容大小提示
  3. 新增 Addressable 初始化操作
  4. 优化 ResourceManager 提供更多的回调参数

2023.08.30

  1. 优化 ResourceManager 更加精简

评论 13
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天喝水了嘛.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值