参考文章:
Unity Optimization Tips « Unity Coding – Unity3D
Learn how to optimize your Unity project | Habrador
Optimizing Graphics in Unity - Unity Learn
Optimizing for Performance - Unity Learn
Unity3D研究院之使用Android的硬件缩放技术优化执行效率 | 雨松MOMO程序研究院
-
导航网格 NavMesh.SamplePosition采样距离由 50 改为 2,25,50三段采样,调用七次的时间从 3.4ms 降至 0.86ms,CPU消耗减少6%左右
-
带有Animator组件的Object频繁的SetActive会导致性能警告,使用移出屏幕的方式代替SetVisible
-
ILRuntime热更框架
-
foreach和使用while(iter.MoveNext())方式的遍历全部替换成for,两者在ILRuntime中都会产生内存垃圾,调用次数太多会触发垃圾回收,经测试foreach大概每次循环分配 47 Bytes,改成for以后不会造成GC Alloc。原生的foreach调用并不会造成GC Alloc,但是执行效率比for低一半。Dictionary类型的遍历可以使用List等可以用for遍历的容器辅助,必要时可以使用辅助容器多存储一份数据专门用来遍历,比如:
int key;
var iter = m_testDict.GetEnumerator();
while(iter.MoveNext()){
key = iter.Current.Key;
}
或者:
int key;
foreach (var item in m_testDict){
key = item.Key;
}
改为:
List<int> keys = m_testDict.Keys.ToList();
int key;
for (int i = 0; i < keys.Count; i++){
key = keys[i];
}
-
在ILRuntime中尽量去掉调用的空函数,比如空Update,因为空函数也会消耗时长,并不会被编译优化掉,真机测试空函数调用一次消耗0.1ms,而且调用空Update时Unity会在内部进行安全检查,检测挂载的对象是否有效。
-
尽量不在Update函数中new对象,缓存频繁使用的对象,比如协程中new出来的 WaitForSeconds(时间常量);减少string类型对象的创建,相同的字符串只创建一次并且缓存使用,减少字符串操作,比如需要拼接字符串来Update一个Text组件,考虑将分割成两个Text组件其中一个来更新需要改变的部分,使用StringBuilder类操作字符串不会产生垃圾内存;
-
尽量不要在Update中使用Find,GetComponent等接口,改成在初始化函数中缓存组件和对象。设置Transform的位置旋转会引起内部所有子对象的计算,如果没有位置和旋转等改变不要对其进行设置,每次调用Transform.position都会导致世界坐标的计算,需要使用时缓存position的值,尽量用Transform.localPosition代替访问position,因为localPosition的值被存储在Transform中,不会每次访问时都计算;Camera.main会在内部调用Find()查找MainCamera,应该缓存下来使用;Mesh.normals[i]的访问会在内部生成新的数组,需要用 Vector3[] meshNormals = mesh.normals这样的方式缓存下来后使用;GameObject.name或 GameObject.tag的访问都会生成新的字符串,如需对比使用GameObject.CompareTag();
-
尽量避免装箱操作,将值类型当作Object类型使用会导致创建新的Object,比如:
int cost = 5; string str = String.Format("Cost: {0}",cost);//cost会被装箱放入新的Object中 yield return 0;//0这样的数值会被装箱,改成 yield return null
-
在Update等频繁调用的接口中使用全局变量(包括static变量),先引用为局部变量再使用,可以将执行效率提升一倍多,因为局部变量在栈上访问,速度可以快一倍多。例如:
for (int i = 0; i < 1000; i++){
bool b = ChatMgr.singleton.IsInPublicChannel;
}
改为:
ChatMgr chatMgr = ChatMgr.singleton;
for (int i = 0; i < 1000; i++){
bool b = chatMgr.IsInPublicChannel;
}
-
Update中调用的逻辑如果不需要逐帧处理,改成使用协程处理或者每隔N帧(N秒)调用,将逻辑错开到不同帧中处理可以避免出现峰值,例如
-
private int interval = 3; void Update(){ if(Time.frameCount % interval == 0){//每三帧调用一次 ExampleExpensiveFunction(); }else if(Time.frameCount % 2 == 1){ AnotherExampleExpensiveFunction(); } }
private float timeSinceLastCalled;
private float delay = 1f;//每秒调用一次
void Update(){
timeSinceLastCalled += Time.deltaTime;
if (timeSinceLastCalled > delay)
{
ExampleGarbageGeneratingFunction();
timeSinceLastCalled = 0f;
}
}
GC: 1.GC Alloc: 任何一次性内存分配大于2KB的选项。每帧都具有20B以上内存分配的选项 。2. Time ms:注意占用5ms以上的选项
GPU:
-
小心使用 Renderer.material,对其进行修改会导致内部复制出新的材质,打破动态合批,如果是单个不需要合批的对象可以使用 Renderer.sharedMaterial 访问材质,否则使用切换材质的方式修改表现。
-
不透明UI层级遮挡时禁用场景摄像机
顶点优化:
-
模型优化,静态动态合批,减面减材质
-
使用LOD
-
使用遮挡剔除Occlusion Culling
像素优化:
-
控制渲染顺序,减少OverDraw,尤其是透明物体
-
尽量减少实时光照
带宽优化:
-
减少纹理大小
-
利用缩放
性能适配开关:
-
多线程渲染
-
分辨率缩放,硬件分辨率缩放,摄像机动态分辨率Unity%20-%20Manual%3A%20Dynamic%20resolution
-
每种后处理效果开关
-
使用简化版本Shader
-
Blit Type 改为Auto