unity性能优化-实际开发中需要注意的点
unity性能优化-实际开发中需要注意的点
在进行下面优化之前,建议先使用 Unity Profiler 定位真实的瓶颈。
优化核心原则:
能缓存的就缓存(变量、组件、材质)。
能不更新就不更新(降低 update 频率,使用事件驱动或Coroutine)。
避免在循环中分配内存(减少 new 关键字,使用对象池)。
1.复杂数学计算,比如mesh顶点数据的创建,大小和归一化属性都非常耗费 CPU(都涉及计算平方根)
可以使用对象池来空间换时间处理,涉及开根号的用平方比较




4.不要再update里面去大量调用Component(新手容易忽略重要)







在C#中,装箱(Boxing)和拆箱(Unboxing)是指值类型与引用类型之间的转换。装箱是将值类型转换为对象类型(通常是将值类型包装在堆上),而拆箱则是从对象类型还原为值类型。
内存分配:
装箱过程中,会在堆上分配内存来存储值类型的数据。堆内存的分配通常比栈内存的分配慢,因为堆内存需要处理垃圾回收(Garbage Collection),而且堆内存分配涉及更多的管理操作。
数据复制:
装箱需要将值类型的数据从栈上复制到新分配的堆对象中。这涉及到数据的复制操作,增加了额外的开销。
垃圾回收:
装箱产生的堆对象最终会由垃圾回收器(GC)回收。垃圾回收是一个耗时的过程,会影响应用程序的性能。
类型检查:
拆箱过程中,需要进行类型检查,以确保对象确实是预期的值类型。这涉及到运行时的类型元数据访问和检查,增加了开销。
数据复制:
拆箱需要将堆上的数据复制回栈上。这同样涉及数据的复制操作,带来了额外的性能开销。
使用泛型:泛型可以减少装箱和拆箱的需要,因为泛型可以在编译时确定类型,而不需要在运行时进行类型转换,但是泛型带来另一个问题,即转换为il2cpp的时候,代码会膨胀,这个后面可以再说
// 使用泛型避免装箱和拆箱 List<intnumbers = new List<int(); numbers.Add(42); int number = numbers[0];
避免频繁的值类型到引用类型的转换:在设计程序时,尽量减少值类型与引用类型之间的转换。如果可能,尽量使用值类型本身来进行计算和处理。
使用struct而不是类:在某些情况下,可以使用struct来代替类,因为struct是值类型,不需要装箱和拆箱。
避免接口调用:接口调用通常需要装箱,特别是在接口接受值类型参数时。尽量避免在接口方法中使用值类型参数
11.GameObject.Instantiate 和 SetParent
1 public class ESDRoadEdgePool 2 { 3 //public int amountToPool = 100; // 池中对象的数量 4 public int amountToPool = 25; 5 6 pooledObjects = new List<GameObject>(); 7 8 for (int i = 0; i < amountToPool; i++) 9 { 10 GameObject obj = Instantiate(objectToPool); // 实例化可以缩减 11 obj.SetActive(false); // 可以预先设置预制体的activeSelf 12 obj.transform.parent = this.transform; // 可以合并到Instantiate中 13 pooledObjects.Add(obj); 14 15 16 GameObject obj = Instantiate(objectToPool, transform); 17 pooledObjects.Add(obj); 18 } 19 }
1 for(int i = 0; i < count; i++) 2 { 3 ... 4 Vector2 p1l_uv = new Vector2(offest.x, uvStart / 10f); 5 Vector2 p1r_uv = new Vector2(offest.y, uvStart / 10f); 6 Vector2 p2l_uv = new Vector2(offest.x, (uvStart + distance) / 10); 7 Vector2 p2r_uv = new Vector2(offest.y, (uvStart + distance) / 10); 8 ... 9 } 10 11 // 可以将对象创建挪到循环外进行复用 12 13 Vector2 p1l_uv = new Vector2(); 14 Vector2 p1r_uv = new Vector2(); 15 Vector2 p2l_uv = new Vector2(); 16 Vector2 p2r_uv = new Vector2(); 17 18 for(int i = 0; i < count; i++) 19 { 20 ... 21 // 同时可以使用Vector3.Set来一次性对向量的每个分量进行赋值 22 p1l_uv.Set(offest.x, uvStart / 10f); 23 p1r_uv.Set(offest.y, uvStart / 10f); 24 p2l_uv.Set(offest.x, (uvStart + distance) / 10); 25 p2r_uv.Set(offest.y, (uvStart + distance) / 10); 26 ... 27 }
1 for (int i = 0; i < len - 1; i++) 2 { 3 ... 4 SquareCell cell = new SquareCell(); 5 ... 6 } 7 8 // 加入对象池 9 public class SquareCellPool 10 { 11 public static SquareCell Allocate() 12 { 13 if (pool.TryDequeue(out var cell)) 14 { 15 totalCount--; 16 return cell; 17 } 18 19 for (int i = 0; i < poolInterval; i++) 20 { 21 SquareCell newCell = new SquareCell(); 22 pool.Enqueue(newCell); 23 totalCount++; 24 } 25 26 if (totalCount > 65535) 27 { 28 Debug.LogError("Pool Out of Count!" + totalCount); 29 return null; 30 } 31 cell = pool.Dequeue(); 32 totalCount--; 33 return cell; 34 } 35 }


在 Unity 中,Camera.main 实际上是调用了 GameObject.FindWithTag("MainCamera"),这是一个非常耗时的操作

发现unity文档里面,这块其实也是做了缓存gameobject

但是缓存肯定还是最快的,因为gameobject也有开销
本文档会陆续补充更新,优化无止境

浙公网安备 33010602011771号