6.YooAsset资源热更新实践

6.YooAsset资源热更新实践


6.1 知识点

导入资源


导入一些资源作为热更新的资源

手写热更新流程

创建场景和主入口,逐步调用YooAsset对应API模拟热更流程

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
using YooAsset;

public class MyYooAssetTest : MonoBehaviour
{
    public EPlayMode playMode = EPlayMode.HostPlayMode; //运行模式
    public string packageName = "DefaultPackage"; //默认包名
    public string packageVersion = ""; //服务器资源版本号
    private ResourcePackage _package = null; //资源包对象

    //网络相关
    public string defaultHostServer = "http://127.0.0.1/CDN/PC/v1.0";
    public string fallbackHostServer = "http://127.0.0.1/CDN/PC/v1.0";

    //下载相关
    public int downloadingMaxNum = 10;
    public int filedTryAgain = 3;
    private ResourceDownloaderOperation _downloader;

    private void Awake()
    {
        DontDestroyOnLoad(gameObject);
    }


    IEnumerator Start()
    {
        yield return null;

        //1.初始化YooAsset
        YooAssets.Initialize();

        // 获取或创建资源包对象
        _package = YooAssets.TryGetPackage(packageName);
        if (_package == null)
        {
            _package = YooAssets.CreatePackage(packageName);
        }

        // 创建远端服务实例,用于资源请求
        IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);

        // 创建联机模式参数,并设置内置及缓存文件系统参数
        HostPlayModeParameters createParameters = new HostPlayModeParameters
        {
            //创建内置文件系统参数
            BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),
            //创建缓存系统参数
            CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices)
        };

        //执行异步初始化
        InitializationOperation initializationOperation =
            _package.InitializeAsync(createParameters);
        yield return initializationOperation;

        // 处理初始化结果
        if (initializationOperation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(initializationOperation.Error);
        }
        else
        {
            Debug.Log("初始化成功-------------------------");
        }


        //2.获取资源版本
        // 发起异步版本请求
        RequestPackageVersionOperation operation = _package.RequestPackageVersionAsync();
        yield return operation;

        // 处理版本请求结果
        if (operation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(operation.Error);
        }
        else
        {
            Debug.Log($"请求的版本: {operation.PackageVersion}");
            packageVersion = operation.PackageVersion;
        }


        //3.获取文件清单
        UpdatePackageManifestOperation operationManifest = _package.UpdatePackageManifestAsync(packageVersion);
        yield return operationManifest;

        // 处理文件清单结果
        if (operationManifest.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(operationManifest.Error);
        }
        else
        {
            Debug.Log("更新资源清单成功-------------------");
        }


        //4.创建下载器
        _downloader = _package.CreateResourceDownloader(downloadingMaxNum, filedTryAgain);
        if (_downloader.TotalDownloadCount == 0)
        {
            Debug.Log("没有需要更新的文件");
            UpdateDone();
            StartCoroutine(UpdateDoneCoroutine());

            yield break;
        }
        else
        {
            int count = _downloader.TotalDownloadCount;
            long bytes = _downloader.TotalDownloadBytes;
            Debug.Log($"需要更新{count}个文件, 大小是{bytes / 1024 / 1024}MB");
        }


        //5.开始下载
        _downloader.DownloadErrorCallback = DownloadErrorCallback; // 单个文件下载失败
        _downloader.DownloadUpdateCallback = DownloadUpdateCallback; // 下载进度更新
        _downloader.BeginDownload(); //开始下载
        yield return _downloader;


        if (_downloader.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(operationManifest.Error);
            yield break;
        }
        else
        {
            Debug.Log("下载成功-------------------");
        }


        //6.清理文件
        // 清理未使用的文件
        var operationClear = _package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
        // 添加清理完成回调
        operationClear.Completed += Operation_Completed;
    }


    //热更新结束
    private async void UpdateDone()
    {
        Debug.Log("热更新结束");

        //跳转场景

        //模拟资源加载
    }

    //热更新结束协程
    IEnumerator UpdateDoneCoroutine()
    {
        yield return null;
    }

    // 单个文件下载失败
    public static void DownloadErrorCallback(DownloadErrorData errorData)
    {
        string fileName = errorData.FileName;
        string errorInfo = errorData.ErrorInfo;
        Debug.Log($"下载失败, 文件名: {fileName}, 错误信息: {errorInfo}");
    }

    // 下载进度更新
    public static void DownloadUpdateCallback(DownloadUpdateData updateData)
    {
        int totalDownloadCount = updateData.TotalDownloadCount;
        int currentDownloadCount = updateData.CurrentDownloadCount;
        long totalDownloadSizeBytes = updateData.TotalDownloadBytes;
        long currentDownloadSizeBytes = updateData.CurrentDownloadBytes;
        Debug.Log($"下载进度: {currentDownloadCount}/{totalDownloadCount}, " +
                  $"{currentDownloadSizeBytes / 1024}KB/{totalDownloadSizeBytes / 1024}KB");
    }

    //文件清理完成
    private void Operation_Completed(AsyncOperationBase obj)
    {
        UpdateDone();
        StartCoroutine(UpdateDoneCoroutine());
    }
}

将汽车图片打包中



同步加载资源

//热更新结束
private async void UpdateDone()
{
    Debug.Log("热更新结束");

    //跳转场景

    //模拟资源加载
    Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车.png")
        .AssetObject as Sprite;
    // Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车")
    //     .AssetObject as Sprite;//后缀名可以省略
    GameObject go = new GameObject();
    go.AddComponent<SpriteRenderer>().sprite = car;
}

开启运行从http下载

复制打包出来的资源到本地资源服务器,启动本地资源服务器


运行游戏,可以看到热更和加载成功

委托异步加载资源

//热更新结束
private async void UpdateDone()
{
    Debug.Log("热更新结束");
    //异步委托资源加载
    AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
    handle.Completed += Handle_Completed;
}
// 异步委托回调
private void Handle_Completed(AssetHandle handle)
{
    Sprite car = handle.AssetObject as Sprite;
    GameObject go = new GameObject();
    go.AddComponent<SpriteRenderer>().sprite = car;
}

协程加载资源

//热更新结束协程
IEnumerator UpdateDoneCoroutine()
{
    // 协程方式加载资源
    AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
    yield return handle;
    Sprite car = handle.AssetObject as Sprite;
    GameObject go = new GameObject();
    go.AddComponent<SpriteRenderer>().sprite = car;
}

Task加载资源

//热更新结束
private async void UpdateDone()
{
    // Task加载资源
    AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
    await handle.Task;

    Sprite car = handle.AssetObject as Sprite;
    GameObject go = new GameObject();
    go.AddComponent<SpriteRenderer>().sprite = car;
}

场景加载

//热更新结束
private async void UpdateDone()
{
    // 场景加载
    string scenePath = "Assets/AB/Scene/Main.unity"; //
    var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
    var physicsMode = LocalPhysicsMode.None;
    bool suspendLoad = false;
    SceneHandle handle = _package.LoadSceneAsync(scenePath, sceneMode, physicsMode, suspendLoad);
    await handle.Task;
    Debug.Log("Scene name is " + handle.SceneName);
}

需要重新打AB包放在服务器后,再运行




预制体加载和创建

//热更新结束
private async void UpdateDone()
{
    //预制体加载和创建
    AssetHandle handle = _package.LoadAssetAsync<GameObject>
        ("Assets/AB/Prefab/Car.prefab");
    await handle.Task;
    GameObject go1 =Instantiate(handle.AssetObject as GameObject);
    Debug.Log("Prefab go1 name:" + go1.name);
    GameObject go2 = handle.InstantiateSync();
    Debug.Log("Prefab go2 name:" + go2.name);
}

需要重新打AB包放在服务器后,再运行





图片子对象加载

//热更新结束
private async void UpdateDone()
{
    //图片子对象加载
    SubAssetsHandle handle =
        _package.LoadSubAssetsAsync<Sprite>("Assets/AB/Image/Farms");
    await handle.Task;
    var sprite = handle.GetSubAssetObject<Sprite>("hen");
    new GameObject().AddComponent<SpriteRenderer>().sprite = sprite;
    Debug.Log("Sprite name: " + sprite.name);
}

需要在2D图片中设置一个子图片叫hen

需要重新打AB包放在服务器后,再运行

卸载相关

//卸载未使用的资源包
var operation = package.UnloadUnusedAssetsAsync();
await operation.Task;

//强制卸载资源包
var operation = package.UnloadAllAssetsAsync();
await operation.Task;

//卸载资源包中某个资源 (要未被引用的,否则无效)
package.TryUnloadUnusedAsset("Assets/GameRes/Panel/login.prefab");

6.2 知识点代码

RemoteServices.cs

using YooAsset;

public class RemoteServices : IRemoteServices
{
    private readonly string _defaultHostServer;
    private readonly string _fallbackHostServer;

    public RemoteServices(string defaultHostServer, string fallbackHostServer)
    {
        _defaultHostServer = defaultHostServer;
        _fallbackHostServer = fallbackHostServer;
    }

    string IRemoteServices.GetRemoteMainURL(string fileName)
    {
        return $"{_defaultHostServer}/{fileName}";
    }

    string IRemoteServices.GetRemoteFallbackURL(string fileName)
    {
        return $"{_fallbackHostServer}/{fileName}";
    }
}

MyYooAssetTest.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
using YooAsset;

public class MyYooAssetTest : MonoBehaviour
{
    public EPlayMode playMode = EPlayMode.HostPlayMode; //运行模式
    public string packageName = "DefaultPackage"; //默认包名
    public string packageVersion = ""; //服务器资源版本号
    private ResourcePackage _package = null; //资源包对象

    //网络相关
    public string defaultHostServer = "http://127.0.0.1/CDN/PC/v1.0";
    public string fallbackHostServer = "http://127.0.0.1/CDN/PC/v1.0";

    //下载相关
    public int downloadingMaxNum = 10;
    public int filedTryAgain = 3;
    private ResourceDownloaderOperation _downloader;

    private void Awake()
    {
        DontDestroyOnLoad(gameObject);
    }


    IEnumerator Start()
    {
        yield return null;

        //1.初始化YooAsset
        YooAssets.Initialize();

        // 获取或创建资源包对象
        _package = YooAssets.TryGetPackage(packageName);
        if (_package == null)
        {
            _package = YooAssets.CreatePackage(packageName);
        }

        // 创建远端服务实例,用于资源请求
        IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);

        // 创建联机模式参数,并设置内置及缓存文件系统参数
        HostPlayModeParameters createParameters = new HostPlayModeParameters
        {
            //创建内置文件系统参数
            BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(),
            //创建缓存系统参数
            CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices)
        };

        //执行异步初始化
        InitializationOperation initializationOperation =
            _package.InitializeAsync(createParameters);
        yield return initializationOperation;

        // 处理初始化结果
        if (initializationOperation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(initializationOperation.Error);
        }
        else
        {
            Debug.Log("初始化成功-------------------------");
        }


        //2.获取资源版本
        // 发起异步版本请求
        RequestPackageVersionOperation operation = _package.RequestPackageVersionAsync();
        yield return operation;

        // 处理版本请求结果
        if (operation.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(operation.Error);
        }
        else
        {
            Debug.Log($"请求的版本: {operation.PackageVersion}");
            packageVersion = operation.PackageVersion;
        }


        //3.获取文件清单
        UpdatePackageManifestOperation operationManifest = _package.UpdatePackageManifestAsync(packageVersion);
        yield return operationManifest;

        // 处理文件清单结果
        if (operationManifest.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(operationManifest.Error);
        }
        else
        {
            Debug.Log("更新资源清单成功-------------------");
        }


        //4.创建下载器
        _downloader = _package.CreateResourceDownloader(downloadingMaxNum, filedTryAgain);
        if (_downloader.TotalDownloadCount == 0)
        {
            Debug.Log("没有需要更新的文件");
            UpdateDone();
            StartCoroutine(UpdateDoneCoroutine());
            yield break;
        }
        else
        {
            int count = _downloader.TotalDownloadCount;
            long bytes = _downloader.TotalDownloadBytes;
            Debug.Log($"需要更新{count}个文件, 大小是{bytes / 1024 / 1024}MB");
        }


        //5.开始下载
        _downloader.DownloadErrorCallback = DownloadErrorCallback; // 单个文件下载失败
        _downloader.DownloadUpdateCallback = DownloadUpdateCallback; // 下载进度更新
        _downloader.BeginDownload(); //开始下载
        yield return _downloader;


        if (_downloader.Status != EOperationStatus.Succeed)
        {
            Debug.LogWarning(operationManifest.Error);
            yield break;
        }
        else
        {
            Debug.Log("下载成功-------------------");
        }


        //6.清理文件
        // 清理未使用的文件
        var operationClear = _package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
        // 添加清理完成回调
        operationClear.Completed += Operation_Completed;
    }


    //热更新结束
    private async void UpdateDone()
    {
        Debug.Log("热更新结束");

        //跳转场景

        //模拟资源加载
        // Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车.png")
        //     .AssetObject as Sprite;
        // // Sprite car = _package.LoadAssetSync<Sprite>("Assets/AB/Image/汽车")
        // //     .AssetObject as Sprite;//后缀名可以省略
        // GameObject go = new GameObject();
        // go.AddComponent<SpriteRenderer>().sprite = car;


        //异步委托资源加载
        // AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
        // handle.Completed += Handle_Completed;


        // task加载资源
        // AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
        // await handle.Task;
        //
        // Sprite car = handle.AssetObject as Sprite;
        // GameObject go = new GameObject();
        // go.AddComponent<SpriteRenderer>().sprite = car;


        //获取所有信息
        // AssetInfo[] assetInfos = package.GetAssetInfos("hot");
        // foreach (var assetInfo in assetInfos)
        // {
        //     Debug.Log(assetInfo.AssetPath);
        // }

        // 场景加载
        // string scenePath = "Assets/AB/Scene/Main.unity"; //
        // var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single;
        // var physicsMode = LocalPhysicsMode.None;
        // bool suspendLoad = false;
        // SceneHandle handle = _package.LoadSceneAsync(scenePath, sceneMode, physicsMode, suspendLoad);
        // await handle.Task;
        // Debug.Log("Scene name is " + handle.SceneName);


        //预制体加载和创建
        // AssetHandle handle = _package.LoadAssetAsync<GameObject>
        //     ("Assets/AB/Prefab/Car.prefab");
        // await handle.Task;
        // GameObject go1 =Instantiate(handle.AssetObject as GameObject);
        // Debug.Log("Prefab go1 name:" + go1.name);
        // GameObject go2 = handle.InstantiateSync();
        // Debug.Log("Prefab go2 name:" + go2.name);


        //图片子对象加载
        SubAssetsHandle handle =
            _package.LoadSubAssetsAsync<Sprite>("Assets/AB/Image/Farms");
        await handle.Task;
        var sprite = handle.GetSubAssetObject<Sprite>("hen");
        new GameObject().AddComponent<SpriteRenderer>().sprite = sprite;
        Debug.Log("Sprite name: " + sprite.name);


        //卸载未使用的资源包
        // var operation = package.UnloadUnusedAssetsAsync();
        // await operation.Task;

        //强制卸载资源包
        // var operation = package.UnloadAllAssetsAsync();
        // await operation.Task;


        //卸载资源包中某个资源 (要未被引用的,否则无效)
        // package.TryUnloadUnusedAsset("Assets/GameRes/Panel/login.prefab");
        //
    }

    //热更新结束协程
    IEnumerator UpdateDoneCoroutine()
    {
        yield return null;
        // 协程方式加载资源
        // AssetHandle handle = _package.LoadAssetAsync<Sprite>("Assets/AB/Image/汽车");
        // yield return handle;
        // Sprite car = handle.AssetObject as Sprite;
        // GameObject go = new GameObject();
        // go.AddComponent<SpriteRenderer>().sprite = car;
    }

    // 单个文件下载失败
    public static void DownloadErrorCallback(DownloadErrorData errorData)
    {
        string fileName = errorData.FileName;
        string errorInfo = errorData.ErrorInfo;
        Debug.Log($"下载失败, 文件名: {fileName}, 错误信息: {errorInfo}");
    }

    // 下载进度更新
    public static void DownloadUpdateCallback(DownloadUpdateData updateData)
    {
        int totalDownloadCount = updateData.TotalDownloadCount;
        int currentDownloadCount = updateData.CurrentDownloadCount;
        long totalDownloadSizeBytes = updateData.TotalDownloadBytes;
        long currentDownloadSizeBytes = updateData.CurrentDownloadBytes;
        Debug.Log($"下载进度: {currentDownloadCount}/{totalDownloadCount}, " +
                  $"{currentDownloadSizeBytes / 1024}KB/{totalDownloadSizeBytes / 1024}KB");
    }

    //文件清理完成
    private void Operation_Completed(AsyncOperationBase obj)
    {
        UpdateDone();
        StartCoroutine(UpdateDoneCoroutine());
    }


    // 异步委托回调
    private void Handle_Completed(AssetHandle handle)
    {
        Sprite car = handle.AssetObject as Sprite;
        GameObject go = new GameObject();
        go.AddComponent<SpriteRenderer>().sprite = car;
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏