版本:Unity 6 (6000.0)
语言:英语
Resources 文件夹
特殊优化

通用优化

优化代码的方式多种多样,就像导致性能问题的原因一样。一般来说,强烈建议开发人员在尝试应用 CPU 优化之前仔细分析他们的应用程序。但是,有一些简单的 CPU 优化方法具有普遍适用性。

通过 ID 访问属性

Unity 在内部不使用字符串名称来访问 Animator、Material 和 着色器在 GPU 上运行的程序。 更多信息
查看 词汇表
属性。为了提高速度,所有属性名称都通过哈希运算转换为属性 ID,实际上使用这些 ID 来访问属性。

因此,在 Animator、Material 或 Shader 上使用 SetGet 方法时,请使用整数值方法,而不是字符串值方法。字符串方法只是执行字符串哈希运算,然后将哈希运算得到的 ID 传递给整数值方法。

从字符串哈希运算创建的属性 ID 在单个运行过程中是确定性的。最简单的方法是为每个属性名称声明一个静态只读整型变量,并在字符串位置使用该整型变量。这些变量会在启动期间自动初始化,无需任何其他初始化代码。

合适的 API 是 Animator.StringToHash 用于 Animator 属性名称,以及 Shader.PropertyToID 用于 Material 和 Shader 属性名称。

使用非分配物理 API

在 Unity 5.3 及更高版本中,引入了所有 Physics 查询 API 的非分配版本。将 RaycastAll 调用替换为 RaycastNonAllocSphereCastAll 调用替换为 SphereCastNonAlloc,依此类推。对于 2D 应用,所有 Physics2D 查询 API 也具有非分配版本。

对 UnityEngine.Object 子类的空比较

Mono 和 IL2CPPUnity 开发的脚本后端,您可以将其用作在构建某些平台的项目时替代 Mono 的方法。 更多信息
查看 词汇表
运行时以特定方式处理从 UnityEngine.Object 派生的类的实例。对实例调用方法实际上会调用引擎代码,该代码必须执行查找和验证操作,以将脚本引用转换为本机引用。尽管很小,但将此类型变量与 null 进行比较的成本远远高于与纯 C# 类进行比较的成本。因此,在紧密循环或每帧运行的代码中避免进行这些空比较。

向量和四元数数学运算以及运算顺序

对于位于紧密循环中的向量和 四元数Unity 用于将旋转表示为数据的标准方法。在编写处理旋转的代码时,通常应使用 Quaternion 类及其方法。 更多信息
查看 词汇表
数学运算,请记住整型数学运算比浮点型数学运算快,而浮点型数学运算比向量、矩阵或四元数数学运算快。

因此,只要允许使用交换律或结合律,请尝试最大程度地减少单个数学运算的成本。


Vector3 x;

int a, b;

// Less efficient: results in two vector multiplications

Vector3 slow = a * x * b;

// More efficient: one integer mult, one vector mult

Vector3 fast = a * b * x;

内置 ColorUtility

对于必须在 HTML 格式的颜色字符串 (#RRGGBBAA) 和 Unity 的原生 ColorColor32 结构之间进行转换的应用程序,通常会使用 Unify Community 中的脚本。该脚本既慢又会导致因字符串操作而大量分配内存。

从 Unity 5 开始,有一个内置的 ColorUtility API,可以有效地执行这些转换。应优先使用内置 API。

Find 和 FindObjectOfType

一般最佳实践是消除生产代码中所有 GameObject.FindObject.FindObjectOfType 的用法。由于这些 API 要求 Unity 遍历内存中的所有 游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路点等。游戏对象的功用由附加到它的组件定义。 更多信息
查看 词汇表
和组件,因此随着项目范围的扩大,它们的性能会迅速下降。

上述规则的例外情况可能是单例对象的访问器。全局管理器对象通常公开一个“instance”属性,并且通常在 getter 中包含一个 FindObjectOfType 调用,以检测单例的预先存在的实例。


class SomeSingleton {

    private SomeSingleton _instance;

    public SomeSingleton Instance {

        get {

            if(_instance == null) { 

                _instance =

                    FindObjectOfType<SomeSingleton>(); 

            }

            if(_instance == null) { 

                _instance = CreateSomeSingleton();

            }

            return _instance;

        }

    }

}

尽管这种模式通常是可以接受的,但重要的是要检查代码并确保访问器在单例对象不存在的 场景场景包含游戏的环境和菜单。将每个唯一的场景文件视为一个独特的关卡。在每个场景中,您放置环境、障碍物和装饰,本质上是以片段的形式设计和构建您的游戏。 更多信息
查看 词汇表
中被调用。如果 getter 不会自动创建缺失的单例的实例,则很常见的是发现查找单例的代码会导致重复调用 FindObjectOfType(通常每帧多次),并对性能造成不良影响。

调试代码和 [conditional] 属性

UnityEngine.Debug 日志记录 API 不会从非开发版本中剥离,并且会在调用时写入日志文件。由于大多数开发人员不打算在非开发版本中写入调试信息,因此建议将仅开发的日志记录调用包装在自定义方法中,如下所示


    public static class Logger {

        [Conditional("ENABLE_LOGS")]

        public static void Debug(string logMsg) {

            UnityEngine.Debug.Log(logMsg);

        }

    }

通过使用 [Conditional] 属性装饰这些方法,Conditional 属性使用的定义将决定装饰的方法是否包含在编译后的源代码中。

如果传递给 Conditional 属性的定义中没有一个被定义,则装饰的方法以及所有对装饰方法的调用都会被编译出来。其效果与将方法和所有对方法的调用包装在 #if … #endif 预处理器块中相同。

有关 Conditional 属性的更多信息,请参阅 MSDN 网站:msdn.microsoft.com.

Resources 文件夹
特殊优化