Unity场景ab包加载压缩(LZ4,LZMA)格式的测试

文章讨论了在Unity中大型场景加载时间过长的问题,通过测试编辑器加载方式和异步同步加载,发现LZ4压缩的性能优于LZMA。同时提到UnityWebRequest自动压缩LZMA格式,推荐使用LZ4以减少网络下载时间和存储空间。

情况

最近场景越来越大,大概800M的场景加载时间可能长达40秒左右,所以需要测试看看发生了什么。

测试环境

测试环境Win10,21thI5-12600KF,32GRam , Nvidia GF RTX2060 32G
Scene1大小:741M

加载代码

首先放上部分的加载场景的代码:

        public float sceneprog;
        public AsyncOperation sceneAsync;
        async public Task LoadScene(string path)
        {
            sceneprog = 0f;
            await Task.Delay(10);
            StreamAssetVer sav = GameJsonMain.inst.Get(path);
            if (sav == null)
            {
                Debug.LogWarning("不存在的场景:" + path);
                return ;
            }
            if (!sav.isab)
            {
                Debug.LogWarning("场景isab必须对勾:" + path);
                return;
            }
            float loadtime = Time.realtimeSinceStartup;
            float computloadtime;
            float ratetime = 0.9f;
            //Application.backgroundLoadingPriority = ThreadPriority.Low;
            
            Debug.Log("LoadScene - begin ..."+ Application.backgroundLoadingPriority);
#if UNITY_EDITOR && TESTRES
            string[] dirs = UnityEditor.AssetDatabase.GetAssetPathsFromAssetBundle(path);
            //Object[] listobj = new Object[dirs.Length];
            if(dirs.Length == 0)
                Debug.LogWarning("找不到这个资源:" + path);
            for (int i = 0; i < dirs.Length; i++)
            {
                string assetPathAndName = dirs[i];
                LoadSceneParameters ls;
                ls = new LoadSceneParameters();
                ls.loadSceneMode = LoadSceneMode.Additive;
                
                sceneAsync = EditorSceneManager.LoadSceneAsyncInPlayMode(assetPathAndName,ls);  // .LoadSceneInPlayMode(assetPathAndName, ls);
                sceneAsync.allowSceneActivation = false;
                while (sceneAsync.progress < 0.9f)
                {
                    sceneprog = sceneAsync.progress;
                    await Task.Delay(100);
                    Debug.Log("load - " + sceneprog);
                }
                //sceneAsync.allowSceneActivation = true;
                //listobj[i] = null;
            }
            sceneprog = 1f;
            //Debug.Log("load1 - " + sceneprog);

            computloadtime = Time.realtimeSinceStartup - loadtime;
            Debug.Log("LoadScene unity- load file time : " + computloadtime);
            return ;
#endif
            Task<UnityWebRequest> task;
            if (sav.include)
            {
                task = streamingAssetsLoader(sav);
            }
            else
            {
                task = AssetsLoader(sav);
            }
            await task;
            UnityWebRequest www = task.Result;
            if (www.result != UnityWebRequest.Result.Success)
            {
                www.Dispose();
                return ;
            }

            computloadtime = Time.realtimeSinceStartup - loadtime;
            Debug.Log("LoadScene - load file time : " + computloadtime);
            //这个LoadFromFileAsync函数的路径不需要file://
            AssetBundleCreateRequest abRequest;
#if UNITY_EDITOR
            abRequest = AssetBundle.LoadFromFileAsync(sav.savepath.Replace(filelink, ""));
#else
            if(Application.platform == RuntimePlatform.Android)
            {
                abRequest = AssetBundle.LoadFromFileAsync(sav.savepath);
            }
            else
            {
                abRequest = AssetBundle.LoadFromFileAsync(sav.savepath.Replace(filelink, ""));
            }
#endif
            abRequest.allowSceneActivation = false;
            
            while (!abRequest.isDone)
            {
                sceneprog = abRequest.progress;
                await Task.Delay(100);
                //Debug.Log("ab:"+ sceneprog);
            }
            abRequest.allowSceneActivation = true;
            AssetBundle ab = abRequest.assetBundle;

            computloadtime = Time.realtimeSinceStartup - loadtime;
            Debug.Log("LoadScene - load AssetBundle time :" + computloadtime);
            //Debug.Log("ab1:ok," + sav.path +" - "+ sav.url);

            sceneAsync = SceneManager.LoadSceneAsync(sav.path, LoadSceneMode.Additive);
            sceneAsync.allowSceneActivation = false;
            while (sceneAsync.progress < 0.9f)
            {
                sceneprog = ratetime + sceneAsync.progress * (1- ratetime);
                await Task.Delay(100);
                //Debug.Log("load:" + sceneprog);
            }

            sceneprog = 1f;
            computloadtime = Time.realtimeSinceStartup - loadtime;
            Debug.Log("LoadScene - load LoadSceneAsync time :" + computloadtime);
            await Task.Delay(300);
            
            //sceneAsync.allowSceneActivation = true;
            //SceneManager.LoadScene(ab.GetAllScenePaths()[0]);
            //Object[] objs = ab. ab.LoadAllAssets();
            www.Dispose();
            ab.Unload(false);
            return ;
        }

大致代码分为2部分,在编辑器下#if UNITY_EDITOR && TESTRES 使用编辑器加载方式。

首先我们直接用编辑器通过UnityEditor.AssetDatabase.GetAssetPathsFromAssetBundle(path);方法来载入,大概需要5.5-7秒时间。为什么AB包需要那么久 ?

异步和同步加载测试

所有有了下面的AB包测试。
因为是AB包下载,所以关闭宏定义TESTRES ,我在想是不是使用了AssetBundle.LoadFromFileAsync来异步加载的,所以比较慢,所以把函数改为了AssetBundle.LoadFromFile,发现:

同步LoadFromFile方式:33秒
异步LoadFromFileAsync方式:38秒

时间都挺长的,发现有一个修改后台CPU级别的函数Application.backgroundLoadingPriority

  • ThreadPriority.Low - 2ms
  • ThreadPriority.BelowNormal - 4ms
  • ThreadPriority.Normal - 10ms
  • ThreadPriority.High - 50ms
    改为了High,发现测试的结果相差不大。

压缩方式的对比

把场景的AB包我又打包成了无压缩格式和LZ4格式。我们看看测试结论
默认我的场景是LZMA格式

LZMA : 38秒
无压缩: 4.8秒
LZ4 : 4.7秒

结论

LZ4的压缩方式解压速度非常快和无压缩相差不大,压缩后大小比无压缩强的多,这种不需要从公网下载资源的推荐LZ4。

知识点

LZMA通过UnityWebRequestAssetBundle加载的LZMA格式AB包将自动重新压缩为LZ4压缩,并缓存在本地文件系统上。而通过自己写的下载方案,则可以调用AssetBundle.RecompressAssetBundleAsync API重新压缩。

参考:

https://zhuanlan.zhihu.com/p/342694796

Gzip_vs_Bzip2_vs_LZMA_vs_XZ_vs_LZ4_vs_LZO

https://segmentfault.com/a/1190000019656656

<think>好的,我现在需要给用户介绍一下Unity中使用LZ4压缩的AssetBundle加载方式。首先,我得回忆一下AssetBundle的基本概念,然后重点转到LZ4压缩的优势和使用方法。 首先,AssetBundle是Unity中用于资源管理的打方式,可以将资源打AB,便于动态加载。而压缩方式常见的有LZMALZ4。用户可能已经知道这些,但需要详细说明LZ4的特点。 LZ4LZMA的区别是什么?LZMA压缩率高但解压速度慢,适合需要小体积的情况,比如下载。而LZ4压缩率稍低,但解压速度快,且支持并行加载,不需要整体解压。这对于运行时性能敏感的场景很重要,比如游戏运行时快速加载资源。 接下来需要说明如何在Unity中配置使用LZ4压缩。我记得在Build AssetBundle的时候,需要选择BuildAssetBundleOptions.ChunkBasedCompression这个选项,这样Unity就会用LZ4压缩。用户可能想知道具体的代码示例,所以需要给出BuildPipeline.BuildAssetBundles的调用方法,并指出这个参数的位置。 然后是加载部分。LZ4压缩AB加载方式是否需要特别处理?因为Unity加载时支持LZ4的实时解压,所以加载方式和未压缩LZMA的不同。对于LZMA,通常需要先解压整个,而LZ4可以流式加载,即从二进制数据中按需加载。可能需要使用AssetBundle.LoadFromFile或LoadFromStream,并且可能需要指定是否使用LZ4解压。不过Unity的新版本可能已经自动处理了这些,所以需要确认正确的API使用方法。 例如,AssetBundle.LoadFromFile在加载LZ4压缩时,是否自动识别并解压?可能需要用特定的参数,比如在Unity 2019.3之后,LoadFromFile会自动处理不同的压缩方式,LZ4。而旧版本可能需要使用LoadFromMemory或者不同的方法。这里需要注意版本差异,但用户可能使用的是较新版本,所以可以推荐使用LoadFromFile,因为它更高效,直接从磁盘读取而不需要全部加载到内存。 另外,加载后的使用流程是否和普通AB一样?比如使用LoadAsset来获取具体资源。这部分应该是一致的,所以不需要特别处理。但可能需要提醒用户,LZ4AB在内存中的占用情况,因为它是按需解压的,可能对内存更友好。 还要注意LZ4AB不能像LZMA那样边下载边解压,所以如果用户需要网络下载,可能需要考虑使用未压缩LZ4HC(高压缩率版本)。不过LZ4HC的压缩率还是比LZMA低,所以需要权衡。 总结一下步骤:构建时选择ChunkBasedCompression,加载时使用LoadFromFile,然后正常加载资源。需要确保用户了解不同压缩方式的优缺点,以及如何正确选择API。 现在需要检查是否有遗漏点,比如加载方式是否需要额外参数,或者不同平台下的差异。例如,在移动平台上,使用LZ4可能更有利于减少加载时间,提升性能。同时,需要提到内存管理,因为LZ4的块压缩允许部分加载,减少内存峰值。 可能还需要对比一下LoadFromFile和LoadFromFileAsync,异步加载对性能的影响,以及如何处理依赖关系。不过用户的问题主要集中在加载方式,所以可能不需要深入讨论依赖项,除非涉及到加载顺序的问题。 最后,确保语言简洁,结构清晰,分步骤说明,并给出示例代码,如构建和加载的代码片段。同时提醒注意事项,比如版本兼容性,压缩方式的选择依据等。</think>好的,我来逐步介绍Unity中使用LZ4压缩的AssetBundle(AB加载方式: --- ### 一、LZ4压缩的特点 1. **与LZMA对比** - LZMA压缩率高,但解压需完整加载,速度慢 - LZ4压缩率略低,但**解压速度快**且支持**流式加载**(无需解压整个) 2. **优势场景** - 适合运行时快速加载(如游戏场景切换) - 支持按需加载资源块,降低内存峰值 --- ### 二、配置LZ4压缩 1. **构建参数设置** 在打AB时,需指定压缩方式为`ChunkBasedCompression`: ```csharp BuildPipeline.BuildAssetBundles( outputPath, BuildAssetBundleOptions.ChunkBasedCompression, // 关键参数 BuildTarget.StandaloneWindows ); ``` 2. **文件特性** - 生成`.manifest`文件记录依赖关系 - 实际AB文件扩展名为`.assetbundle` --- ### 三、加载LZ4 AB的步骤 #### 1. 加载AB文件 使用`LoadFromFile` API,Unity会自动处理LZ4解压: ```csharp // 同步加载 AssetBundle bundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/ab名称"); // 异步加载(推荐避免卡顿) AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path); yield return request; AssetBundle bundle = request.assetBundle; ``` #### 2. 加载资源 ```csharp GameObject prefab = bundle.LoadAsset<GameObject>("资源名称"); Instantiate(prefab); ``` #### 3. 卸载资源 ```csharp bundle.Unload(false); // 保留实例化对象 // 或 bundle.Unload(true); // 彻底卸载所有资源 ``` --- ### 四、关键注意事项 1. **内存管理** LZ4支持按需加载资源块,建议: - 使用`AssetBundle.LoadAssetAsync`异步加载大资源 - 及时调用`Unload`释放未用资源 2. **平台兼容性** - Android/iOS均支持LZ4 - 注意文件路径差异:Android需使用`Application.streamingAssetsPath + "!assets/"`的特殊路径 3. **性能优化** - 对频繁加载AB使用**缓存机制** - 使用`Addressables`系统可简化LZ4 AB管理 --- ### 五、适用场景建议 | 压缩方式 | 适用场景 | 内存效率 | |----------|--------------------------|----------| | LZ4 | 频繁加载的大资源 | ★★★★ | | LZMA | 需网络下载的小体积 | ★★ | | 不压缩 | 需要极速加载的常驻内存资源 | ★★★★★ | --- 如果有具体使用场景(如热更新策略、资源依赖管理),可以进一步讨论优化方案! 🎮
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Thinbug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值