当前位置:   article > 正文

关于Unity加载优化,你可能会遇到这些问题_unity打开项目很慢

unity打开项目很慢

关键词

资源加载、卸载

实例化实例化

资源管理方法

一、资源加载

Q1Shader 是独立打包的。如果我在开始游戏的时候加载一次,以后切换场景时就不用每次加载了吗?

确切地说,要实现后续Shader不加载开销,需要满足以下两个条件:

(1)包含Shader的AssetBundle文件常驻内存;

(2)着色器已完全加载;

只要满足这两个条件,后续加载的依赖于这些Shader的GameObject就会被直接使用,没有加载和解析的开销。

Q2:假设有两个接口Panel A和Panel B,它们都依赖一个共享的Atlas C,打包AssetBundle的时候,如果我们单独打包,使用的时候可以不卸载AssetBundle C吗?如果 Atlas C 在 Panel A 和 B 加载之前被加载到内存中,但 AssetBundle C 被卸载,那么在 Panel A 和 B 加载和实例化时会不会有问题?

是的,不只是Panel和Atlas,只要A或B依赖于C,就必须保证A或B在加载或实例化时,C的AssetBundle必须存在于内存中。如果之前卸载了C对应的AssetBundle,那么在加载或实例化A和B时,引擎将无法自动将C绑定到A和B上使用。

关于AssetBundle依赖打包,我们在之前的分享【揭开AssetBundle庐山真面目】中已经提到过。

Q3:什么是Loading.UpdatePreloading?为什么突然这么高?一般有什么办法可以优化吗?

这是 Unity 引擎的主要加载功能。切换场景或主动动态加载资源时,此项一般较大。一般来说,加载资源越复杂,Loading.UpdatePreloading的耗时就越多。

在优化之前,必须定位该函数的CPU使用瓶颈。下图是我们的案例项目,UWA评估报告中Loading.UpdatePreloading函数的整体CPU分配情况。通过这些堆栈信息unity 异步加载场景,开发团队可以一目了然地看到函数的耗时分配,从而进行有针对性的优化。

Q4:关于Loading.ReadObject成本高,有什么推荐的方法吗?

Loading.ReadObject是Unity引擎的资源加载函数,一般在切换场景和加载API调用时发生,包括纹理、网格、Material、Shader、AnimationClip等资源。如果你发现这个值太高,建议大力优化加载的相关资源。对于每一个资源的加载,我们都以专题的方式进行总结和分享。建议您先检查以下内容:

加载模块纹理

加载模块的网格

加载模块的着色器

加载模块的动画片段

Q5:我使用了Shader.WarmupAllShaders操作,加载资源后依然出现CreateGPUProgram。 (Shaders都在一个AssetBundle文件中,都是常驻内存,不会被删除)是否需要使用ShaderVariantCollection来加载Shaders?

上述问题的可能是:Shader被打包到不同的AssetBundle中,而WarmupAllShaders只能对当前内存中的Shader进行预热。如果稍后加载另一个 Shader,仍然会发生 CreateGPUProgram 操作。

建议开发团队使用UWA的资源检测工具检测AssetBundle中Shader的具体打包情况,看看是否存在冗余打包问题。

Q6:我们对 UWA 进行了性能测试,发现在 Android 上同步加载 AssetBundle 资源会消耗大量 CPU。因此,我们最近对资源加载方式进行了比较大的调整。大多数资源都是异步加载的。但是这里有个问题我要问:AssetBundle.LoadAsync和WWW加载方式都可以异步加载AssetBundle,但是两者的API特性也不同。目前,WWW 消耗更多的内存。这两种方法哪个更推荐?物种?

AssetBundle.LoadAsync是获取AssetBundle对象后加载资产;而WWW加载是为了获取AssetBundle,两者的作用是不同的。

开发人员可能想了解几种用于异步加载 AssetBundle 的 API 之间的区别。相关接口如下:

  1. new WWW
  2. WWW.LoadFromCacheOrDownload

这两种方式的具体区别请参考《你应该知道的AssetBundle管理机制》中的“AssetBundle加载进阶”部分。

二、资源卸载

Q1:切换场景时,卸载前一个场景总感觉很费时间。您有什么推荐的解决方案吗?

这是Unity在切换场景时调用Resources.UnloadUnusedAssets函数造成的。通常,开销相对较大。建议开发团队通过UWA性能测试,进一步定位加载模块中卸载场景的罪魁祸首。

Q2:Resources.UnloadUnusedAssets() 不仅限于 Resources.Load 资源,还包括 AssetBundle.Load 资源,对吧?

是的,Resources.UnloadUnusedAssets 也可以卸载 AssetBundle.Load 加载的资源,但前提是对应的 AssetBundle 已经调用了 Unload(false) 并且没有被引用。

Q3:我用UGUI做的界面有背景图。游戏中不进行任何处理。关闭并销毁界面后,图片还在内存中,但是我已经调用了Resources.UnloadUnusedAssets,如下图所示。我的预设没有标记为 AssetBundle,它们是在资源路径中加载的。请问是什么原因?

在使用Resources.Load加载UI界面的情况下,即使“关闭并销毁该界面”,Resources.UnloadUnusedAssets仍然无法卸载对应的图集。因为此时Resources.Load加载的Prefab仍然引用了图集。

在这种情况下,我们的建议是手动调用Resources.UnloadAssets手动释放图集(可以通过Sprite.texture找到对应的图集),当UI界面重新实例化时,图集也会自动执行重新加载。

Q4:使用 Resources.UnloadAsset 释放未实例化的 Object 会导致以下错误:Unload Assets 只能用于单个资产,不能用于 GameObject's/Components 或 AssetBundles。如何解决?

Resources.UnloadAsset 只能释放非GameObject和Component资源,如Texture、Mesh等真实资源。 Prefab加载的Object或Component,无法通过该函数释放。

Q5:我在 Profiler 中看到 GC.MarkDependencies 的 CPU 消耗是 900ms+,虽然在退出战斗时调用了 Resources.UnloadUnusedAssets();但冻结仍然很明显。您有什么推荐的解决方案吗?

GC.MarkDependencies 的消耗是由 Resources.UnloadUnusedAssets 引起的。该功能的主要目的是查找和卸载不再使用的资源。游戏场景越复杂,资源越多,这个函数的开销就越大,一般在300~2000ms的范围内。所以我们在UWA报告中加入了这个功能的调用监控,让大家更好的控制它的调用。

对于这个功能的优化,我们建议一方面控制场景中不必要的资源量,同时通过UnloadAsset及时卸载不再使用的资源,以减少耗时压力Resources.UnloadUnusedAssets。

以后我们会在加载模块的相关文章中详细讲解Resources.UnloadUnusedAssets的性能问题,敬请期待。

Q6:我有一个使用图集的 UI 预设,当我打包它时,我将图集和 UI 输入到 AssetBundle 中。我在加载生成GameObject后立即卸载了AssetBundle对象,但后来销毁GameObject时,发现图集依然存在。什么情况?

这很有可能。 unload(false) 卸载 AssetBundle 不会破坏其加载的资源。您必须调用 Resources.UnloadUnusedAssets。关于 AssetBundle 加载的详细解释,请参考我们之前的文章:AssetBundle 管理机制你应该知道的。

Q7:如果先Destroy Prefab,再Unload Prefab中使用的AssetBundle,这个顺序会不会有问题?在手机上测试的时候,发现内存会一直存在,不会被释放;如果反转,则可以释放。另外,我调用了 Resources.UnloadUnusedAssets();在Destroy期间,这会影响最终结果吗?

这确实可能发生。当 Resources.UnloadUnusedAssets(); ,如果没有执行 AssetBunlde 的 Unload 操作,从 AssetBunlde 加载的资源仍然不会被卸载,因为它们被 AssetBunlde 引用。开发团队可以尝试Dest​​ory然后做AssetBunlde的Unload,最后做Resources.UnloadUnusedAssets(); .

三、管理

Q1:Unity 现在不能将场景与 NavMesh 数据或 Lightmap 数据分开,可以吗?我想先加载一个干净的场景,然后动态切换不同的光照贴图和NavMesh网格数据,有什么办法吗?

光照贴图可以从场景中分离出来,并且可以通过 AssetBundle 动态加载。建议将Lightmap打包为通用资源,动态加载后,通过LightmapSetting接口将场景的Lightmap整体替换掉。目前,Navmesh 确实离不开现场。但是在Unity 5.x版本之后,引擎已经允许通过LoadLevelAdditive加载多个场景来加载NavMesh。因此,研发团队可以尝试在多个场景中预烘焙NavMesh,然后通过LoadLevelAddtive或类似API进行加载,从而达到动态加载NavMesh的效果。

Q2:点击应用到游戏画面出现的时间是否与Resource目录的大小有关?

从点击应用到第一次出现应用画面,加载时间主要与两个方面有关:

1、资源文件夹中的资源数量。游戏启动时,Unity引擎会为Resources文件夹中的资源建立一个搜索树,存放其对应的索引unity 异步加载场景,方便后续资源加载。一般来说,Resouces文件夹中的资源越多,构建时间越长,应用启动越慢;

2、第一个场景的资源加载和相关代码的初始化。如果第一个场景资源较多,脚本初始化任务繁重,应用的启动时间会比较慢。

Q3:AssetBundle解压出来的资产在使用过程中会占用一定的内存。我们现在想尝试使用两种加载方式:(1)AssetBundle加载相关资源后,缓存资源,卸载AssetBundle文件;(2)缓存AssetBundle文件,使用相关资源稍后)然后直接加载资源。这两种方法你推荐哪个更好?

对于Unity 5.3版本之前的项目,建议通过LoadFromCacheOrDownload或LoadFromFile加载AssetBundle,既可以减少Assetbundle对象的内存占用,又可以保持AssetBundle之间的链接关系和资源,方便后续加载有依赖的Prefabs。

Unity5.3之后的项目,可以从AssetBundle包本身处理。即:使用 LZ4 格式的 AssetBundle 文件,而不是默认的 LZMA 格式。也可以满足上述要求。稍有不足​​的是,LZ4格式的AssetBundle文件比LZMA格式的文件占用空间稍大。

如果是依赖包,我们还是建议将依赖的共享资源AssetBundle文件缓存在内存中。虽然内存有一定的增加,但上述方法可以大大减少内存使用量,缓解内存使用量。压力。

Q4:如何动态加载Navmesh?

目前Navmesh不支持动态加载,只能随场景加载。因此,可以考虑将带有Navmesh的场景打包成AssetBundle,然后在AssetBundle中使用LoadLevel加载场景。

导航网格的动态加载已经在 Unity 的路线图中。目前Navmesh是和场景绑定的,也就是说只能通过LoadLevel加载场景(不支持>>LoadLevelAdditive的加载方式),自动加载对应的Navmesh数据。替代方案是:将多个“Scene Prefabs”的Navmesh组合到同一个场景中并烘焙(不相互重叠),然后将这些“Scene Prefabs”分离成单独的场景;在运行阶段,Navmesh随着第一个场景的变化,一次性加载,对于其他场景对象,可以通过LoadLevelAdditive加载对应的场景。缺点是场景对象为了对齐Navmesh,在场景制作过程中不能出现坐标重叠,仅供参考。

在Unity5.x下,Lightmap的动态加载需要在烘焙时通过脚本编写每个物体的Lightmapindex和Lightmapscaleoffset,在运行时动态加载后重新设置。因为Lightmapindex和Lightmapscaleoffset信息目前是和场景绑定的,存储在Lightmapsnap.assets中,所以发布的时候也是放在场景信息中的,所以不会记录在Prefab上。

Q5:对于同一个纹理,Prefab 会生成多个实例。这个纹理会有多个副本吗?

这道题需要查看Prefab的具体加载方式。如果只是通过Resources.Load加载,就不会有纹理的多份,但是如果通过AssetBundle加载,每个Prefab都是一个AssetBundle,并且纹理没有打包依赖,那么纹理资源确实会在记忆。有多个副本。如果你发现内存中有多个相同纹理的副本,并且通过 AssetBundle 文件加载资源,建议将 AssetBundle 上传到 UWA 网站()UI界面,其资源检测工具可以帮助开发团队有效地检测和定位 AssetBundle。资源过剩。

四、实例化实例化

Q1:第一次执行GameObject.Instantiate时,有些资源会卡住(当时是加载实例),有些复杂的资源在执行GameObject.Instantiate时甚至会卡住70多毫秒首次。导致明显滞后,有什么好的解决办法吗?

Instantiate 的冻结与三个部分的开销有关:相关资源加载、脚本组件序列化和构造函数执行,大部分原因是相关资源加载引起的。因此,我们的建议如下:

1、通过 Profiler 查看 Instantiate 的具体 CPU 分配情况;

2、如果是资源加载导致的性能瓶颈,一方面简化资源以缓解CPU耗时压力,另一方面通过AssetBundle预加载资源依赖包,也就是这里Instantiate的整体消耗。分时,分摊到前一帧执行(如切换场景等),使Instantiate实例化操作的本地耗时更流畅;

3、如果性能瓶颈是脚本组件的序列化造成的,可以尝试减少脚本中的序列化信息;

4、如果构造函数的执行造成的性能瓶颈只能通过策略来避免,比如降低Instantiate的调用频率。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/125540
推荐阅读
相关标签
  

闽ICP备14008679号