优化学习笔记
1.var obj = Resources.Load("Sphere1") as GameObject
o1 = Instantiate(obj);
float r = Random.Range(0, 1f);
prop.SetColor(Shader.PropertyToID("_Color"), new Color(r, 1, 1, 1));
Destroy(o1);
这个时候虽然摧毁了o1,但是设置颜色的时候unity会创建新的材质,材质并没有跟着一起销毁,会造成资源内存泄漏,需要下使用Destroy(o1.GetComponet<Renderer>().material);把材质球也销毁先
2.Unload(false)只会卸载ab,从ab包中加载的资源不会跟着卸载,使用Unload(true)可以一起卸载,不过需要主要是否还有其他模块在引用这个ab的资源,不然会导致误卸载而出现白图
3.mipMap开启会越增加1/3内存,会按照分辨率/4递减存放不等等级图片,根据摄像机距离来调整使用哪张图片,2d不开启,3d相关的建议开启,同时Quality里面的Texture Quality可以拿来进一步优化mipMap,有三种分级Full Half Quarter,Full就是不变,Half就是把第一张原图去掉,最近也是使用第二张开始,Quarter就是把第一第二张都去掉,这个可以拿来做机型分级,高端机用Full,中端机用Half类推,Texture Streaming能动态调整MipMap加载数量
4.Project Settings里面的Player选项的Optimize Mesh Data,勾选后会自动去掉网格没使用到的顶点,能节约内存
5.开启静态合批是使用空间换取时间的方式,会增大内存,所以如果内存有性能瓶颈需要考虑关闭
动画相关优化:
1.Resample Curves:默认开启,重采样,去掉一些关键帧,欧拉角全变四元数,动画曲线也会有相应变化
2.动画压缩:
3.剔除Scale曲线
4.降低精度:unity默认是有精度为10位的float存储,可以改成精度为3位的float,单纯降低精度并不会降低内存,用一个float占用的大小都一样的,但是降低精度会使得动画的精度发生改变,有些曲线的细微起伏变成了直线,因此在Optimal的压缩格式下存储的格式变成了Constant,也就是这条曲线可以用一个常数表示,从而降低了内存占用,如果降低精度后曲线存储格式没改变那并不会发生变化。动画文件本身的大小也会变低
可以改成Clamped Auto就会变直线
音频资源:
1.Force To Mono
效果可能有影响,但是减少内存占用,具体和压缩格式有关系
2.Load Type
Streaming模式虽然占用内存最小,但是因为需要用到内存缓冲,如果短时间内重复播放一个音频反而会导致重复播放期间内容飙升很多,也会占用一部分cup资源,所以需要注意使用方式
3.Compression Format
字体资源:
1.字体瘦身:去掉一些不常用或者没必要的字体以减小文字的资源大小,可使用FontPruner或者FontSubsetGUI工具修改
2.压缩字体纹理:主要是TMP生成的SDF字体是纹理形式,无法直接修改
粒子系统资源:
1.粒子数量:当前播放的数量才会影响内存占用,与最大播放数量无关,但是即使播放的数量是0,也还是会占用一小部分内容
2.未播放的粒子:预设里面没有用的粒子系统需要删除,不然也会占用内存
第四章:托管堆内存
4.1 Mono堆内存具体分配
4.1.1:驻留内存过高:例如使用了UIParticles这个插件,插件代码里面会根据最大粒子数去创建数组,如果最大粒子数设置的比较大,远远超出了当前需要播放的粒子数,那么就会导致创建的数组过大,其实不需要那么多,可以在预设上把最大粒子数降低到正常范围内
4.1.2:持续分配内存过高:UIParticles插件存在持续分配内存过高的问题,1万帧差不多分配了300M内存,一般是建议1万帧分配的内存总量不要超过50M,这个定位可以通过UWA GOT的函数总体堆栈信息来查询出问题的函数
Mission 3
第一章:Mecanim动画
Mecanim动画系统是Unity目前使用的主要的动画系统,非常适合于人形动画。它针对人形角色提供了一套特殊的工作流,包括Avatar的创建以及Muscles肌肉的调节,同时也提供了可视化的Animator编辑器,可以快捷预览和创建动画片段,此外它还能够更加方便的创建状态机以及状态之间Transition的转换,在动画混合方面也提供了容易操作的混合树功能。
Mecanim动画造成的性能压力主要体现在CPU端的耗时上,它的主要耗时函数是PreLateUpdate.DirectorUpdateAnimationBegin和PreLateUpdate.DirectorUpdateAnimationEnd。针对Mecanim动画耗时的优化主要是优化这两个函数的耗时,确认导致这两个函数耗时较高的原因。一般而言,动画系统顶层函数的自身耗时占比是较低的,所以优化的重点在于它的子堆栈的耗时。下面也会逐一讲解。
1.1 Active Animator数量
Active Animator指的是在场景中会造成Animator.ApplyOnAnimatorMove调用的Animator对象,它的数量越多,Mecanim动画的耗时也就越高。建议将Animator的Culling Mode设置为Cull Update Transform,这样当Animator所控制的对象不在任何相机的可见范围内时,Unity会停止更新该动画对象的Retarget、IK和Write Transform也就是写回骨骼节点的Transform,但是仍然是会更新动画状态机和根运动。但是需要注意的是,如果是用于UI动画的Animator,则其Culling Mode一定要设置为Always Animate,否则会有表现上的错误。
1.2 Optimize Game Objects
Optimize Game Objects是针对骨骼节点数量较多的模型给出的优化项,勾选上之后可以减少骨骼节点的Transform回传C#端的耗时。它的耗时主要体现在Animator.WriteJob函数下,当Animator.WriteJob函数的耗时占比较高时,需要确认场景中的模型的Optimize Game Objects选项是否有勾选上。开启后MeshSkinning.CalcMatrices(计算骨骼数据)会从主线程转移到子线程上运行
1.3 Apply Root Motion
Apply Root Motion的选项只有在动画播放过程中需要位移的对象才有必要勾选,关闭它可以节省部分耗时。它的耗时主要体现在Animator.ApplyBuiltinRootMotion函数,当该函数的耗时占比较高时,需要确认场景中Animator对象是否都需要产生位移。
1.4 Compute Skinning
Compute Skinning选项是指使用GPU来计算骨骼动画的蒙皮的选项,但是实际上勾选上后性能表现会有所下降,不建议勾选。
1.5 Animator.Initialize
Animator.Initialize是指Animator所在的对象被激活时会触发的调用,需要确认它的调用频率和耗时情况是否合理。Animator.Initialize会在含有Animator组件的GameObject被Active或Instantiate时触发,耗时较高,因此在战斗中不建议过于频繁地对含有Animator的GameObject进行Deactive/ActiveGameObject操作,可以改为Disable并且移除Animator组件的方式来进行Animator动画的激活禁用。
第二章:Lagacy动画
Legacy动画是Unity的老的动画系统,目前也还有一些适合它的场景,比如说用于一些简单的UI动画等。它的主要耗时函数是PreLateUpdate.LegacyAnimationUpdate,一般而言,优化的重点也是它的子堆栈的耗时。
2.1 Animation.Sample
Animation.Sample的调用次数显示了场景中实际在更新的Animation对象的数量,而它的父节点Animation.Update的调用次数则是显示了场景中存在的Animation对象的数量。因此,优化Legacy Animation动画耗时则是要减少Animation.Sample的调用次数。
2.1.1:CullingType
设置成Based On Renderers,屏幕外的动画就会不更新
2.1.2:激活/实例化
2.1.3 AddClip
这些操作都是为了使Animation.Sample耗时降低
Mission 4 物理模块
第一章:物理模块耗时
Unity物理系统的性能瓶颈主要体现在CPU端的耗时,它的主要耗时函数为FixedUpdate.PhysicsFixedUpdate。在开启Physics设置时,它的主要耗时堆栈是Physics.Processing和Physics.Simulate,需要针对这两个函数进行优化。首先会影响这两个函数耗时的是它们的调用次数,调用次数越多则耗时也就越高,而物理函数的调用次数受到Time设置里Maximum Allowed Timestep和Fixed TimeStep的影响会存在一个调用次数上限。其中,Maximum Allowed TimeStep决定fxf了单帧物理最大调用次数,该值越小,单帧物理最大调用次数越少;Fixed TimeStep决定了FixedUpdate的更新间隔,该值越大,每帧物理更新调用次数越少。此外,当游戏陷入卡顿,帧耗时较高时,物理函数的调用次数也会随之增加。
1.1 Collision的产生
了解Collision的产生条件,哪些碰撞体之间会产生Collision。每当产生Collision,Unity对象会在OnTriggerEnter、OnCollisionEnter等函数中收到碰撞事件的相关信息并执行其中的相关逻辑。因此有必要确认当前Collision的产生情况是否符合预期,是否存在不必要的Collision。
1.2 Trigger的替代方案
Collider.Bounds实现替代Trigger,避免使用Unity的物理模块。Trigger触发是比较方便的能够使用非物理模拟的方式来进行替换的一种Collision,使用C#逻辑来替代掉Trigger可以降低部分物理模块的耗时。
1.3 Physics Layer的设置
Physics Layer中取消不必要的层之间的碰撞检测,避免多余的Contacts的产生。
1.4 物理更新次数
Time设置会影响到物理更新次数的上限,而具体的物理更新次数会受到帧耗时的影响。
1.5 Auto Simulation
不需要使用物理模块时直接关闭Auto Simulation以节省物理模拟的耗时,而如果需要使用射线检测时只需要开启Auto Sync Transform即可。在使用NGUI时也是可以这么做的,但是开启了Trigger和Collision的粒子系统要想有正常的物理表现则不可以关闭Auto Simulation。
1.6 RaycastCommand
射线检测在场景中碰撞体数量较多时同样会产生较高的CPU端耗时,可以使用Unity提供的RaycastCommand来进行Job化的异步射线检测,减少主线程的耗时。