优化代码的方法有很多,导致性能问题的原因也有很多。通常,强烈建议开发人员在尝试应用CPU优化之前仔细分析他们的应用程序。然而,有几个简单的CPU优化是普遍适用的。
通过Id定位属性
Unity内部不会使用 string 名来定位 Animator Material Shader 属性。为了提高速度,所有的属性名都被 Hash 到属性id中,而这些id实际上是用来定位属性的。
因此,无论何时在 Animator Material Shader 上使用 Set 或 Get 方法,请使用整数值方法而不是字符串值方法。字符串方法执行字符串 Hash,然后将 Hash 后的ID转发给整数值方法。
从字符串 Hash 创建的属性id在单次运行过程中是确定的。使用它们的最简单方法是为每个属性名声明一个静态只读整型变量,并使用整型变量代替字符串。这些在启动时自动初始化,不需要进一步的初始化代码。
Animator属性名:Animator.StringToHash Material&Shader属性名: Shader.PropertyID
使用无GC的物理Api
在Unity 5.3及以后的版本中,引入了所有物理查询api的非分配版本。用 RaycastNonAlloc 代替 RaycastAll 调用,用 SphereCastNonAlloc 代替 SphereCastAll 调用。对于2D应用程序,也存在所有Physics2D查询api的非分配版本。
Null比较 UnityEngine.Object 子类
Mono / IL2CPP 运行时以一种特别的方式看待继承自 UnityEngine.Object 的子类实例。调用实例的方法实际上会调用引擎代码,它必须执行查找和验证以将 script 引用转换为 native 引用。
与纯c#类相比,将这种类型的变量与null进行比较需要更多的资源消耗。出于这个原因,在紧密循环或每帧运行的代码中避免这些null比较。
Vector & Quaternion & Order of operation
对于紧密循环中的 Vector Quaternion,请记住,integer 数学比 float 数学快,float 数学比 vector、matrix 或 quaternion 数学快。
因此,只要交换或关联算法允许,尝试最小化单个数学运算的资源强度:
1 | Vector3 x; |
内置的 ColorUtility
对于必须在 html 格式的颜色字符串(#RRGGBBAA)和Unity的原生颜色和 Color32 结构之间进行转换的应用程序来说,统一使用来自社区的脚本是很常见的。这个脚本速度很慢,并且由于字符串操作而导致大量内存分配。
在Unity 5中,有一个内置的ColorUtility API可以有效地执行这些转换。应该优先使用内置API。
Find & FindObjectOfType
最佳实践:在生成环境中不使用 GameObject.Find Object.FindObjectOfType ,因为这些API会在内存中迭代遍历所有的 GameObject 和 Component ,所以随着项目范围的扩大,它们会迅速变得性能不佳。
Camera locators
Unity内部的 Camera.main 属性会调用 Object.FindObjectWithTag ,一个 Object.FindObject 的变体,访问此属性并不比调用 Object.FindObjectOfType 更有效。
如果代码必须处理主摄像头,强烈建议做以下两件事之一:
- 在
Start和OnEnable回调中访问Camera.main,并缓存结果。 - 构造一个
CameraManager类,它可以提供或注入对活动摄像机的引用。
Debug隔离
UnityEngine.Debug 日志API并不会在非开发环境的构建中剔除,大多数开发者并不打算在非开发构建中打印Debug信息,推荐包装仅开发可用的自定义日志方法。
通过使用 [Conditional] 属性修饰这些方法,condition属性使用的定义或定义可以确定修饰过的方法是否包含在编译的源代码中。例如:
1 | public static class Logger { |
如果传递给Conditional属性的所有定义都没有定义,则编译出修饰方法和对修饰方法的所有调用。如果方法和对方法的所有调用都包装在#if…#endif预处理器块中,则会发生相同的效果。