# UnityOptimize **Repository Path**: lzmxqh/unity-optimize ## Basic Information - **Project Name**: UnityOptimize - **Description**: 学习:Unity性能优化 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-01-22 - **Last Updated**: 2024-03-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: 性能优化 ## README # UnityOptimize ### 一、查看性能工具 #### 1. Stats窗口 #### 2. 性能分析器Profiler *** ### 二、内存 * 压缩自带类库 * 将暂时不用的以后还需要使用的物体隐藏起来而不是直接 Destroy 掉 * 释放 AssetBundle 占用的资源 * 降低模型的片面数,降低模型的骨骼数量,降低贴图的大小 * 使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),使用预设(Prefab) * 警惕配置表内存占用 * 减少容器扩容 或者 利用 string字符串拼接 等一系列产生 GC 的操作 * 检查 ShaderLab 内存占用 * 避免使用 Standard 材质,做好相应的 variant skip * 排查项目冗余的 shader * 使用 shader_feature 替代 multi_compile,这样只会收集项目里真正使用的变体组合,避免变体翻倍 * 检查纹理资源的尺寸、格式、压缩方式、mipmap、Read & Write 选项使用是否合理 * 检查 Mesh 资源的 Read & Write 选项、顶点属性使用是否合理 * 代码级别的检查,如 Cache 预分配空间、容器的 Capacity、GC 等 * 使用 Profiler 定位下 GC,特别是 Update 类函数里的。如:字符串拼接、滥用容器等 * 合理控制 RenderTexture 的尺寸 * 优化动画 Animation 的压缩方式、浮点精度、去除里面的 Scale 曲线数据 * 减少场景 GameObject 节点的数量,最好支持工具监控 *** #### 1. 简述GC(垃圾回收)产生的原因,并描述如何避免 GC垃圾回收机制,避免堆内存溢出,定期回收那些没有有效引用的对象内存 GC优化,就是优化堆内存,减少堆内存,即时回收堆内存 GC 归属于 CLR 避免: 1. 减少 new 的次数 2. 字符串拼接使用 string builder,字符串比较先定义一个变量存储,防止产生无效内存 3. list,new时候,规定内存大小 4. 如果要射线检测,应该使用避免 GC 的方法 xxxNoAlloc 函数 5. foreach 迭代器容易导致GC(已修复),使用 For 循环 6. 使用静态变量,GC 不会回收存在的对象,但静态变量的引用对象可能被回收 7. 使用枚举替代字符串变量 8. 调用 gameobject.tag == "XXX" 就会产生内存垃圾;那么采用 GameObject.CompareTag() 可以避免内存垃圾的产生 9. 不要在频繁调用的函数中,反复进行堆内存分配,比如 OnTriggerXXX,Update 等函数 10. 在 Update 函数中,运行有规律的但不需要每一帧执行的代码,可以使用计时器,比如 1秒 执行一次某些代码 *** #### 2. 对象池 对象池存放需要被反复调用资源的一个空间,比如游戏中要常被大量复制的对象,子弹、敌人、以及任何重复出现的对象 当程序中有重复实例化并不断摧毁的对象时需要使用对象池进行优化 每个需要使用对象池的对象都需要继承对象池的基类对象 销毁操作是通过对象池接口提供的回收接口 场景结束时要及时销毁整个对象池 *** #### 3. UI ##### 3.1. UI字体拆分 项目中字体占了很大的空间,如果有几个不同的字体一起展示在屏幕上,会消耗较大的内存。 提取常用字体,另外生成一个字体文件,让字体文件变小,内存变少,最终使得加载变快 如登陆场景中的数字和字母,注册登陆后取名的3000个常用字 ##### 3.2. UI贴图设置优化 对于不透明的纹理,关闭它的alpha通道 如果不需要运行时读取图片像素信息,则禁用读写,否则纹理内存消耗会增加一倍 禁用 Mip Streaming,mipmap主要为远处的物体生成较清晰的贴图,UI贴图用不到 根据具体情况限制贴图尺寸 ##### 3.3. UI图集优化 充分利用图集空间,图集低于1/3,合并提升利用率 图集大小控制1024*1024,原图过大可以进行拆分,放入图集中 使用九宫格来减少原图大小 *** #### 4. 贴图透明通道分离,压缩格式设为 ETC / PVRTC 使用 DXT5 作为贴图压缩格式,希望能减小贴图的内存占用,但在移动平台的显卡是不支持的。因此对于一张 1024x1024 大小的 RGBA32 贴图,虽然 DXT5 可将它从 4MB 压缩到 1MB,但系统将它送到显卡之前,会先用 CPU 在内存里将它解压成 4MB 的 RGBA32 格式(软件解压),然后再将这 4MB 送进显存。于是在这段时间里,这张贴图就占用了 5MB 内存和 4MB 显存;而移动平台往往没有独立显存,需要从内存里抠一块作为显存,于是原以为只占 1MB 内存的贴图实际却占了 9MB。 所有不支持硬件解压的压缩格式都有这个问题。安卓上硬件支持最广泛的格式是 ETC,苹果上则是 PVRTC。但这两种格式都是不带透明(Alpha)通道的。因此需要将每张原始贴图的透明通道分离出来,写进另一张贴图的红色通道里。这两张贴图都采用 ETC / PVRTC 压缩。渲染的时候,将两张贴图都送进显存。同时修改 NGUI 的 Shader,在渲染时将第二张贴图的红色通道写到第一张贴图的透明通道里,恢复原来的颜色: ```C# fixed4 frag(v2f i) : COLOR { fixed4 col; col.rgb = tex2D(_MainTex, i.texcoord).rgb; col.a = tex2D(_AlphaTex, i.texcoord).r; return col * i.color; } ``` *** #### 5. 关闭贴图的读写选项 Unity 中导入的每张贴图都有一个启用可读可写(Read / Write Enabled)的开关,对应的程序参数是 TextureImporter.isReadable。 选中贴图后可在 Import Setting 选项卡中看到这个开关。只有打开这个开关,才可以对贴图使用 Texture2D.GetPixel,读取或改写贴图资源的像素,但这就需要系统在内存里保留一份贴图的拷贝,以供 CPU 访问。 一般游戏运行时不会有这样的需求,因此可以关闭所有贴图的开关,只在编辑中做贴图导入后处理(比如对原始贴图分离透明通道)时打开它。 这样,上文提到的 1024x1024 大小的贴图,其运行时的 2MB 内存占用又可以少一半,减小到 1MB。 *** #### 6. 包体 > 压缩图片、压缩音频、压缩自带库文件、分段下载、字体裁剪 *** ### 三、CPU > 对象池、分帧运算、数据缓存、算法 * 逻辑和表现尽可能分离开,这样逻辑层的更新频率可以适当降低些 * 对于一些热点函数,如 mmo 的实体更新、实例化,使用分帧处理,分摊单帧的时间消耗 * 做好同屏实体数量、特效数量、距离显隐等优化 * 完善日志输出,避免没必要的日志输出,同时警惕日志字符串拼接 * 使用 骨骼烘焙 + GPU Skinning + Instance 降低 CPU 蒙皮骨骼消耗 和 DrawCall * 开启模型的 Optimize Game Objects 减少节点数量 和 蒙皮 更新消耗 * UI拼预制,做好动静分离,对于像血条名字这种频繁变动的ui,做好适当的分组 * 减少 C# 和 lua 的频繁交互,尽量精简两者传递的参数结构 * 使用 StringBuilder 优化字符串 拼接的 GC 问题 * 删除非必要的脚本功能函数,特别是 Update / LateUpdate 类高频执行函数,因为会产生 C++ 到 C# 层的调用开销。对于 Update 里需要用到的组件、节点等提前 Cache 好。 * 场景里频繁使用的资源 或 数据结构 做好 资源复用 和 对象池 * 对于频繁显示隐藏的UI,可以先移出到屏幕外,如果长时间不显示再进行 Deactive * 合理拆分 UI 图集,区分共用图集和非共用图集,共用图集可以常驻内存,非共用图集优先按功能分类,避免资源冗余。 * 使用IL2CPP,编译成C++版本能极大的提升整体性能。 * 避免直接使用 Material.Setxxx/Getxxx 等调用,这些调用会出发啊材质实例化消耗,可以考虑使用 SharedMaterial / MaterialPropertyBlock 代替 * 合并 Shader 里的 Uniform 变量 *** ### 四、GPU * 合理规划好渲染顺序,避免不必要的 OverDraw,如:地形(容易被其他物件遮挡)、天空盒放到较后渲染 * 分辨率缩放,对于填充率出现瓶颈时,这个是最简单高效的 * 避免使用 GrabPass 抓屏,不是所有硬件都支持,加之数据回拷和没法控制分辨率性能很差,可考虑使用 CommandBuffer.blit 去优化 * 控制好地形的 Blend 层数,控制在 4 层以内,考虑到地形一般屏占面积大、贴图采样次数多,对于中低画质考虑不用 NormalMap * 做好物件、树、角色的LOD * 避免使用 RenderWithShader 类方式来定制 DepthTexture,可以考虑 Camera 的 public void SetTargetBuffers(RenderBuffer colorBuffer, RenderBuffer depthBuffer); 进行优化 * 检查 Shader 的 VertexInput 和 VertexOutput 是否存在冗余是数据。如:顶点色、多套UV * 警惕项目里非必要的双面材质,对于需要局部双面的地方通过加面解决 * Shader 里使用 fixed、half 代替 float,理论上除 position、uv、一些涉及 depth 相关计算使用 float 外,其他都应该使用 fixed(主要是颜色值)、half * 对于角色皮肤这种不是特别明显的效果,考虑使用预积分这种低成本方案 * 对于 frag 里的计算过程,如果可以抽出来放到 CPU 应用层、顶点阶段的优先放这里计算。需要注意放到顶点阶段引起的平滑过渡问题。如:eyeVec导致高光过渡问题。 * 镜面反射类效果避免使用反射相机 +RT 的实现,考虑使用 SSR、CubeMap 类实现 * 避免使用实时阴影,如若使用要合理控制下分辨率和阴影距离。考虑使用 Projector * 使用统一的后处理框架代替多个 Image Effect,可以共用模糊函数,减少 blit 操作。另外 unity 自带的 Postprocessing V2 支持 Volume,性能还是不错的。 * Shader 里避免使用 分支、循环,sin、tan、pow、log 等复杂数学运算 * Unity 自带的遮挡剔除因为 CPU 消耗和 内存 占用较高,加之不能 Instancing,不太适合移动平台,可以考虑静态预计算(缺点是不支持动态物体)、Hi-Z 等优化方案。 * 减少 alpha test 材质的使用,如若使用注意减小面积、控制渲染顺序 *** #### 1. DrawCall > CPU准备好需要绘制的元素,对底层图形程序接口进行调用的过程, > 每次引擎准备数据并通知GPU的过程称为一次 Draw Call。 > DC越高对显卡的消耗就越大。 #### 2. UI ##### 2.1. UI动静分离 以 canvas 为节点,设置 动态 canvas 和 静态 canvas, 实际项目静态元素较多,动态元素较少, 动静分离后,CPU在重绘和合并Mesh时消耗就会减少。 ##### 2.2. 拆分过重的UI 将界面中隐藏的独立界面做一次拆分 对二次显示内容,如部分动效图标,小窗口等做二次拆分 降低UI实例化,初始化时的CPU消耗 ##### 2.3. UI预加载 UI实例化到场景中的过程:网格合并,组件初始化,渲染初始化,图片加载,界面逻辑调用等,消耗大量CPU 预加载:把资源加载到内存、UI实例化和UI初始化的CPU消耗放在loading等待时间线上 ##### 2.4. UI图集Alpha分离 为了压缩图集的时候,将透明通道分离出去不压缩,既缩小了内存,图像显示又不会太失真 UGUI已经在Unity内部完成了Alpha分离,NGUI则需要另外自己分离Alpha 首先:打成两张图集,分别为RGB888和Alpha8的png,也可以使用程序提取分离 其次:修改NGUI的原始着色器,绑定主图和绑定Alpha图 然后:将NGUI的着色器shader中相应修改为新的颜色通道和透明通道 最后:NGUI工具类也要相应修改编辑几个类 最终:主图和Alpha图合成新图片替换原来的图片 ##### 2.5. ScrollView 滚屏优化 不停滚动会导致合并重构网格 使用对象池进行优化,实时判断是否有UI元素被移出画面,对元素重复利用 ##### 2.6. 网格重构优化 UGUI图在改变颜色或alpha后,导致对Mesh重构的优化 UGUI的Mesh的合并机制是拥有相同的材质球的Mesh合并在一起才能达到最佳效果 一个材质球对应一个图集,只有相同图集内的图片才需要合并在一起 使用自定义材质球,替代UGUI设置顶点颜色和新材质球来达到透明效果,避免了网格重构 另外切换贴图,也可以更换自定义材质球中的贴图 材质球会增加一次dc, 另外使用透明效果会与原有的UGUI产生的材质球的透明贴图形成不同的渲染排序 可以改变自定义shader中的渲染次序 RenderQueue 来解决半透明排序问题 这个半透明物体的排序问题,归根结底是无法写入深度数据的问题 ##### 2.7. UI展示与关闭优化 关闭UI时选择隐藏,不销毁,这样在打开和关闭时,只消耗了少量CPU在展示和隐藏上 ##### 2.8. 高低端机型画质优化 使用两套UI贴图,高清,低清,两套图,两套Prefab, NGUI和UGUI高清HD和SD切换的流程可以通过编写脚本程序一键搞定 模型和特效使用不同质量(三角面数)的预制体,预制体命名后缀做加载区分,区分等级 阴影根据使用情况进行区分 整体贴图渲染质量进行区别对待 使用QualitySetting的API来对阴影和贴图渲染质量做操作 通过程序来区分机型,ios通过机型判断,安卓通过CPU型号,内存,系统,平均帧综合判断 ##### 2.9. UI图集拼接归类 重复利用的公用资源放common图集 同一个界面UI资源放一个图集 ##### 2.10. 调整UI层级 不同图集材质等,会导致UI合批中断 ##### 2.11. UI图集的作用 图集就是碎片合成大图 降低内存,减少DC UI图集有合批没有的优点,就是热更新的时候因为小文件变少了,所以会快一些 UI图集就是UI的动态合批 UI图集完成合批的条件:深度 贴图 材质 => 排序好的列表 当前这个UI对象 依次和前面对比 是否 贴图 和 材质 的 ID 相同 决定是否合批 *** #### 3. 层消隐距离技术 如果场景中存在大量小“物件”,则可以使用“层消隐距离”来优化场景; “层消隐距离”就是在比较远的距离将小物体剔除以减少绘图调用的数量 比如:可以一个大型场景中,高大型的物体任然可见,但是一些小装饰内容(小狗、车子之类的则可以隐藏) *** #### 4. LOD是什么,优缺点是什么? LOD(Level of detail)多层次细节,是最常用的游戏优化技术。 它按照模型的位置和重要程度决定 物体渲染的资源分配,降低非重要物体的 面数 和 细节度,从而获得高效率的渲染运算。 这就是说,根据摄像机与模型的距离,来决定显示哪一个模型,一般距离近的时候显示高精度多细节模型,距离远的时候显示低精度低细节模型,来加快整体场景的渲染速度。 * 作用:优化GPU * 缺点:同一模型要准备多个模型,消耗内存。 * 特点:以内存做消耗来优化GPU *** #### 5. 合批 一次 Draw Call 中批量处理多个物体。只要物体的变换和材质引用相同,GPU 就可以按完全相同的方式进行处理,即可以把它们放在一个 Draw Call 中。 注意:简单来说在一个 Canvas 下,需要相同的材质,相同的纹理以及相同的Z值。 例如UI上的字体 Texture 使用的是字体的图集,往往和我们自己的UI图集不一样,因此无法合批。还有UI的动态更新会影响网格的重绘,因此需要动静分离。 *** #### 6. 静态合批 将 static 的静态物体(永远不会移动、旋转和缩放),如果相同材质球,面数在一定范围之内。unity会自动合并成一个 batch 送往GPU处理。 原理:在开始阶段把需要静态批处理的 GameObject 进行一次网格合并操作,然后把这个合并之后的大网格保存,后续都是用这个网格而不需要再进行合并。 在预处理阶段,把一些材质相同的模型的顶点统一变换到世界空间坐标下,并且新构建一个大的 VB 把数据保存下来,在绘制时,就会把这个大的 VB 提交上去,只需要设置一次渲染状态,再进行多次DC 绘画出每个子模型。所以 Static Batching 是不会减少 DC 的,但由于只修改了一次渲染状态,依然可以减少 CPU 的消耗。而且在渲染前,也可以进行视椎体剔除,减少顶点着色器对不可见的顶点的处理次数,提高GPU的效率。 操作方法:把要进行静态批处理的 GameObject 在 Inspector 面板右上角的 Static 勾选(实际上只需要勾选 Batching Static 即可) * 优点:因为只需要进行一次,所以性能会比动态批处理要好。 * 缺点:使用静态合批需要额外的内存开销来存储合并后的几何数据。 因为需要额外维护多一份数据,所以包体会变大,占用的内存也会变多,不能有超级大量的相同模型(如:森林里的树) *** #### 7. 动态合批 如果动态物体共用着相同的材质,那么 Unity 会自动对这些物体进行批处理。动态批处理操作是自动完成的,并不需要进行额外的操作。 原理:Unity 会检测哪些 GameObject 使用了同一个共享材质,然后去合并这些使用了同一个共享材质的网格顶点数据,形成一个新的大网格,然后传给显存,直接渲染这个大网格就相当于渲染了所有的被合并的小网格,而这只需要一次 DrawCall。 在每一帧运行时,计算相同材质的模型,把他合并批次进行渲染。动态合批只需要设置一次渲染状态,且能减少 DrawCall 次数。 优点:不用自己做任何事情,Unity会在游戏中自动进行动态批处理,只要满足下述条件。 * 顶点属性要小于900。例如,如果shader中需要使用顶点位置、法线和纹理坐标这三个顶点属性,那么要想让模型能够被动态批处理,它的顶点数目不能超过300。因此,优化策略就是 Shader 的优化,少使用顶点属性,或者模型顶点数要尽可能少。《UnityShader入门精要》不同版本可能有区别。 * 多 Pass 的 Shader 会中断批处理。 * 使用 LightingMap 的物体需要小心处理。为了让这些物体可以被动态批处理,需要保证它们指向 LightingMap 中的同一位置。 *** #### 8. 动态合批 跟 静态合批 的区别 * 动态批处理一切都是自动的,不需要做任何操作,而且物体是可以移动的,但是限制很多。 * 静态批处理自由度很高,限制很少,缺点可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。 * 静态合批发生在加载场景的时候。 * 动态合批发生在游戏运行的时候。 *** #### 9. Mask 和 RectMask2D 的区别 Mask 使用模板缓冲来实现区域切除逻辑(Stencil),会占用两个DC。它实现最初设置模板缓存会给 Mask 添加一个特殊的材质,并且以像素为单位存储是否需要显示最后还原模板缓存,这两次操作各增加一次 DC。它可以和其他 Mask 子物体进行合批,如果两个 Mask 重叠了,那就不能进行合批,会产生额外的 DC。 RectMask2D 继承自 IClipper 接口,内部主要实现的就是一个方法来实现了区域的切除逻辑,本身是不占用DC的,完全遮住的情况下不会绘制顶点和面,不参与深度运算不占用 DC(和 Mask 的最大区别)。缺点:它无法和 RectMask2D 的子物体进行合批,只能和自身的子物体进行合批(注:如果本身带了 Image 组件的话是可以进行合批的) RectMask2D 只能矩形,要不同形状对的遮罩还是得 Mask,所以 RectMask2D 并不一定完全好,他在特定情况下无法合批。 *** #### 31. 光照的优化 #### 32. 阴影的优化 #### 33. 纹理贴图、精灵图的优化 #### 34. 使用图集Sprite Atlas对图片进行优化 #### 35. 碰撞器的优化 #### 36. 模型的优化 #### 37. LOD技术 *** ### 五、网络 > 使用压缩率高的通信协议 > 数据流图优化 *** ### 六、简述优化脚本的方法 * 减少 GetComponent、find 等查找函数在 Update等循环函数中的调用、go.CompareTag 代替 go.tag * 减少 SendMessage 等同步函数调用;减少字符串连接;少用 linq * 大资源改为异步加载 * 合理处理协程调用 * 将 AI、网络 等放在单独线程 * 发布优化:关闭log、剔除代码 * 伪随机 * 脚本挂载类改为 Manager 等全局类实现 * lua 中尽量不实现 update、fixedupdate 等循环函数,lua 和 C# 互调用的效率比较低 *** ### 七、简述优化内存管理的方向 * 按照不同资源、不同设备管理资源生命周期,Resources.Load 和 Assetbundle 统一接口,利用引用计数来管理生命周期,并打印和观察生命周期。保证资源随场景而卸载,不常驻内存,确定哪些是预加载,哪些泄漏 * 内存泄漏(减少驻留内存):Container 内资源不 remove,调用 Resources.UnloadUnusedAssets 是卸载不掉的;对于这种情况,建议直接通过 Profiler Memory 中的 Take Sample 来对其进行检测,通过直接查看 WebStream 或 SerializedFile 中的 AssetBundle 名称,即可判断是否存在“泄漏”情况;通过 Android PSS / iOS instrument 反馈的 APP线程内存 来查看 * 堆内存过大:避免一次性堆内存的过大分配,Mono的堆内存一旦分配,就不会返还给系统,这意味着 Mono 的堆内存是只升不降的。常见:高频调用new;log输出; * CPU占用高:NGUI 的重建网格导致 UIPanel.LateUpdate(按照静止、移动、高频移动来切分);NGUI 锚点自身的更新逻辑也会消耗不少CPU开销,需要修改NGUI的内部代码,使锚点只在必要时更新。加载用协程;控制同一个UIPanel中动态UI元素的数量,数量越多,所创建的 Mesh 越大,从而使得重构的开销显著增加。比如,战斗过程中的HUD血条可能会大量出现,此时,建议将运动血条分离成不同的UIPanel,每组UIPanel下5~10个动态UI为宜。这种做法,其本质是从概率上尽可能降低单帧中UIPanel的重建开销。 * 资源冗余:AssetBundle 打包打到多份中;动态修改资源导致的 Instance 拷贝多份(比如动态修改材质,Renderer.meterial,Animation.AddClip) * 磁盘空间换内存:对于占用 WebStream 较大的 AssetBundle 文件(如 UI Atlas 相关的 AssetBundle 文件等),建议使用 LoadFromCacheOrDownLoad 或 CreateFromFIle 来进行替换,即将解压后的 AssetBundle 数据存储于本地 Cache 中进行使用。这种做法非常适合于内存特别吃紧的项目,即通过本地的磁盘空间来换取内存空间 *** ### 八、简述美术优化的方向 * 建立资源审查规范和审查工具:PBR材质贴图制作规范、场景制作资源控制规范、角色制作规范、特效制作规范;利用AssetPostprocessor建立审查工具 * 压缩纹理、优化精灵填充率、压缩动画、压缩声音、压缩UI(九宫格优于拉伸);严格控制模型面数、纹理数、角色骨骼数 * 粒子:录制动画代替粒子、减少粒子数量、粒子不要碰撞 * 角色:启用Optimize Game Objects减少节点,使用(SimpleLOD、Cruncher)优化面数。 * 模型:导入检查Read/Write only、Optimize Mesh、法线切线、color、禁用Mipmap * 压缩纹理问题:压缩可能导致色阶不足;无透明通道用ETC1,现在安卓不支持ETC2已不足5%,建议放弃分离通道办法。 * UI:尽可能将动态UI元素和静态UI元素分离到不同的UIPanel中(UI的重建以UIPanel为单位),从而尽可能将因为变动的UI元素引起的重构控制在较小的范围内; 尽可能让动态UI元素按照同步性进行划分,即运动频率不同的UI元素尽可能分离放在不同的UIPanel中; 尽可能让动态UI元素按照同步性进行划分,即运动频率不同的UI元素尽可能分离放在不同的UIPanel中; * ugui:可以充分利用canvas来切分不同元素。 * 大贴图会导致卡顿,可以切分为多个加载。 * iOS使用mp3压缩、Android使用Vorbis压缩 *** ### 九、简述优化物理系统的方法 * 不需要移动的物体设为Static * 不要用Mesh碰撞,角色不用碰撞体 * 触发器逻辑优化 * 寻路频率、AI逻辑频率 、Fixed Timestep、降帧到30 * 出现卡顿的复杂计算,例如寻路、大量资源加载 可以用分帧或者协程异步来处理 *** ### 十、简述UI资源如何优化 #### 1.纹理资源优化 1. 单个纹理尺寸为2的幂次方,最大尺寸1024x1024(内存优化) 2. 纹理加载方式:流式纹理加载Texture Streaming 3. 不通过增加纹理大小来增加细节,而是通过增加细节贴图DetailMap或增加高反差保留 4. 纹理压缩:可以使用ETC1+Alpha(安卓),ETC2(安卓),PVRTC(ios),ASTC 6x6 5. ASTC更优,内存大小相同的情况下,纹理效果最好,加载速度最快,包体最小 6. 纹理MipMap:逐级减低分辨率来保存纹理副本,相当于纹理LOD 7. 内存变大1/3,通过Mipmap开启可以限制不同平台加载不同level层级的贴图 *** #### 2.UI纹理图集 1. UI图集最大尺寸为1024x1024 2. 重复利用的公用资源放common图集(drawcall优化) 3. 同一个界面UI资源放一个图集(drawcall优化) 4. 使用九宫格来减少原图大小(内存优化) 5. 提高图集利用率,原图分辨率需要包含在1024x1024尺寸下,如果原图过大需要进行拆分,放入图集中 6. 图集合并提升利用率,如果图集低于1/3 就要合并图集