0
点赞
收藏
分享

微信扫一扫

使用AddressableURL重定向功能实现从StreamingAsset加载缓存

认真的老去 2022-01-04 阅读 22
缓存unity

Unity的Addressable系统提供的两种打包方式:localremote。然而这与国内手游的更新逻辑有所不同。使用local方式能够进到安装包但是无法更新;使用了remote方式可以进行更新,但是不在安装包中,游戏第一次运行时需要进行下载。
手游更新逻辑通常是,安装包中保存完整的资源,第一次运行时无需再次进行长时间的更新,可以直接进入游戏,之后依然可以更新资源。这就需要资源在打包的时候放进StreamingAsset中,更新方式又需要设置为remote
Unity提供了InternalIdTransformFunc函数实现了URL重定向功能,这样资源请求时可以指定到StreamingAsset中。
实现过程分为:

  1. 打包过程中获取bundle文件及其列表文件
  2. 将bundle文件和列表文件拷贝到AA的LocalBuildPath文件夹中,AA的LocalBuildPath会在BuildPlayer时拷贝到StreamingAsset
  3. 设置Addressables.InternalIdTransformFunc函数,重定向资源的URL

环境准备

  1. Packages:
    • “com.unity.addressables”: “1.19.15”,
    • “com.unity.nuget.newtonsoft-json”: “2.0.2”,用来写入json文件
  2. remote资源服务器直接使用了AAS自带的Host

工程设置

  1. 开启AAS的远程打包更新配置

    • 设置Player Version Override
    • 禁用Catalog自动更新(可选,会手动初始化)
    • 开启BuildRemoteCatalog选项
      在这里插入图片描述
  2. 添加图片到工程,并且配置到AAS中
    在这里插入图片描述

代码部分

  1. 资源构建

public static class BuildEditor
{
    [MenuItem("Tools/Build Resource #%&t")]
    public static void BuildResource()
    {
        var buildRootDir = GetAASRemoteBuildDir();
        //删除原有文件夹
        GlobalFunc.DeleteFolder(buildRootDir);
        //执行打包逻辑
        AddressableAssetSettings.BuildPlayerContent(out var result);
        BuildResource_CacheBundleList(result);
        BuildResource_CopyBundleDirToAAS();
        Debug.Log($"build resource finish");
    }

    /// <summary>
    /// 获取RemoteBuild文件夹路径
    /// </summary>
    /// <returns></returns>
    public static string GetAASRemoteBuildDir()
    {
        var settings = AddressableAssetSettingsDefaultObject.Settings;
        var profileSettings = settings.profileSettings;
        var propName =
            profileSettings.GetValueByName(settings.activeProfileId, AddressableAssetSettings.kRemoteBuildPath);
        var remoteBuildDir = profileSettings.EvaluateString(settings.activeProfileId, propName);
        return remoteBuildDir;
    }

    /// <summary>
    /// 生成BundleCache文件列表
    /// </summary>
    /// <param name="result"></param>
    static void BuildResource_CacheBundleList(AddressablesPlayerBuildResult result)
    {
        var buildRootDir = GetAASRemoteBuildDir();
        var buildRootDirLen = buildRootDir.Length;
        List<string> allBundles = new List<string>();

        var filePathList = result.FileRegistry.GetFilePaths().Where((s => s.EndsWith(".bundle")));
        foreach (var filePath in filePathList)
        {
            var bundlePath = filePath.Substring(buildRootDirLen + 1);
            allBundles.Add(bundlePath);
        }

        var json = JsonConvert.SerializeObject(allBundles);
        File.WriteAllText($"{buildRootDir}/{GlobalFunc.Caching}.json", json);
    }

    /// <summary>
    /// 拷贝Bundle到AAS,并随AAS进包
    /// </summary>
    static void BuildResource_CopyBundleDirToAAS()
    {
        var remoteBuildDir = GetAASRemoteBuildDir();
        var aaDestDir = $"{Addressables.BuildPath}/{GlobalFunc.Caching}";

        GlobalFunc.DeleteFolder(aaDestDir);
        Directory.CreateDirectory(aaDestDir);
        //拷贝bundle到aa文件夹
        var allSrcFile = Directory.EnumerateFiles(remoteBuildDir, "*.*", SearchOption.AllDirectories);
        foreach (var srcFile in allSrcFile)
        {
            var fileName = Path.GetFileName(srcFile);
            var destFile = $"{aaDestDir}/{fileName}";
            File.Copy(srcFile, destFile);
        }

        Debug.Log($"Bundle拷贝完毕:{remoteBuildDir}==>{aaDestDir}");
    }
}
  1. 运行时代码
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SocialPlatforms;
using UnityEngine.UI;

public class GameMain : MonoBehaviour
{
    public RawImage Raw;
    private List<string> _bundleCacheList = new List<string>();

    // Start is called before the first frame update
    IEnumerator Start()
    {
        Debug.Log(Application.persistentDataPath);
        yield return Addressables.InitializeAsync();
        var checkHandle = Addressables.CheckForCatalogUpdates(false);
        yield return checkHandle;
        if (checkHandle.Result.Count > 0)
        {
            yield return Addressables.UpdateCatalogs(checkHandle.Result);
        }

        Addressables.Release(checkHandle);

        //从StreamingAsset中获取bundle列表文件
        var bundleCacheFileURL = $"{Addressables.RuntimePath}/{GlobalFunc.Caching}/{GlobalFunc.Caching}.json";
#if (UNITY_IOS || UNITY_ANDROID) && !UNITY_EDITOR
        var url = bundleCacheFileURL;
#else
        var url = Path.GetFullPath(bundleCacheFileURL);
#endif
        var request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();
        if (!string.IsNullOrEmpty(request.error))
        {
            Debug.LogError(request.error);
        }

        Debug.Log($"BundleCache:{request.downloadHandler.text}");
        _bundleCacheList = JsonConvert.DeserializeObject<List<string>>(request.downloadHandler.text);
        Addressables.InternalIdTransformFunc = Addressables_InternalIdTransformFunc;

        //开始业务逻辑
        StartLogic();
    }
    
    string Addressables_InternalIdTransformFunc(IResourceLocation location)
    {
        if (location.Data is AssetBundleRequestOptions)
        {
            if (_bundleCacheList.Contains(location.PrimaryKey))
            {
                var fileName = Path.GetFileName(location.PrimaryKey);
                //使用LogError用来测试是否使用了StreamingAsset缓存
                Debug.LogError($"StreamingAssetCache:{location.PrimaryKey}");
                return $"{Addressables.RuntimePath}/{GlobalFunc.Caching}/{fileName}";
            }
        }

        return location.InternalId;
    }

    void StartLogic()
    {
        var h = Addressables.LoadAssetAsync<Texture>("pic/1.jpg");
        h.Completed += handle => { Raw.texture = h.Result; };
    }
}

运行测试

  1. 构建安装包,并运行。可以看到使用了缓存资源
    在这里插入图片描述

  2. 再次构建资源后,重新运行程序。可以看到图片资源已经更新,并且没有出现缓存的log。
    在这里插入图片描述

  3. 经测试安卓端正常运行,其中需要注意的是Caching.json时,根据平台不同需要对URL的处理也不同

#if (UNITY_IOS || UNITY_ANDROID) && !UNITY_EDITOR
        var url = bundleCacheFileURL;
#else
        var url = Path.GetFullPath(bundleCacheFileURL);
#endif

最后附上工程链接:https://github.com/ZiJinZiMing/Demo4InternalIdTransformFunc

举报

相关推荐

0 条评论