版本:Unity 6 (6000.0)
语言:英语
分析工具
理解 Unity 中的优化

分析分析器跟踪

在你分析应用程序时,有一些常见的你可能会遇到的问题。此页面概述了调查一些常见性能问题原因的方法。

剖析启动跟踪

在查看启动时间跟踪时,有两种关键方法要检查:UnityInitApplicationGraphicsUnityLoadApplication。这两个方法是项目配置、资产和代码可以在其中影响启动时间的两个主要位置。

请注意:你的应用程序的启动时间在不同平台上各不相同。在大多数平台上,启动在 启动画面 出现时发生。

Instruments trace of an example Unity project running on an iOS device
在 iOS 设备上运行的范例 Unity 项目的 Instruments 跟踪

在上图中,这是在 iOS 设备上运行的范例 Unity 项目的 Instruments 跟踪,在特定于平台的 startUnity 方法中,请注意 UnityInitApplicationGraphicsUnityLoadApplication 方法。

UnityInitApplicationGraphics 执行很多内部工作,例如设置图形设备并初始化很多 Unity 内部系统。它还通过加载包含在 Resources 系统中的所有文件的索引来初始化 Resources 系统

Unity 的 Resource 系统 包括项目 Assets 文件夹中的 Resources 文件夹中的数据里的每个 asset 文件。这包括 Resources 文件夹的子文件夹中的任何文件。因此,初始化 Resources 系统所需的时间随着你应用程序项目 Resources 文件夹中文件的增加而增加。

UnityLoadApplication 包含用于加载和初始化项目中第一个 场景场景包括游戏环境和菜单。可将每个独特的场景文件视为一个独立等级。在每个场景中,你可以布置你的环境、障碍物和装饰,从本质上来说是以分块的方式设计和构建你的游戏。 更多信息
参见 词汇表
的方法,其中包括反序列化和实例化显示第一个场景所需的数据,例如编译 着色器在 GPU 上运行的程序。 更多信息
参见 词汇表
、上传纹理以及实例化 游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、导航点等。游戏对象的函数由附加到它的组件定义。 更多信息
参见 词汇表
。此外,Unity 会执行第一个场景中所有 MonoBehaviourAwake 回调。

这些流程意味着,如果某个项目第一个场景中的 Awake 回调存在任何长时间运行的代码,则该代码可能导致项目初始启动时间变慢。解决此问题的办法是消除慢速代码,或在应用程序的生命周期中的其他位置执行该代码。

分析运行时跟踪记录

对于在初始启动时间之后捕获的性能分析跟踪记录,最主要的关注点是 PlayerLoop 方法。这是 Unity 的主循环,其中包含的代码每帧运行一次。

Instruments trace of an example Unity project
Unity 示例项目工具跟踪记录

上方的屏幕截图展示了 PlayerLoop 中对性能产生最大影响的几个方法。注意:PlayerLoop 中,方法的名称在不同的 Unity 版本之间可能不同。

PlayerRender 是运行 Unity 渲染系统的方法。其中包括剔除对象、计算动态批处理以及向 GPU 提交绘制指令。所有图像效果或基于渲染的脚本回调(例如 OnWillRenderObject)也会在此处运行。一般来说,当项目具有交互性时,这应该是 CPU 时间的最大耗用方。

BaseBehaviourManager 调用 CommonUpdate 的三个模板版本。这些模板版本在当前场景中的活动游戏对象附加的 MonoBehaviour 内调用某些回调

  • CommonUpdate<UpdateManager> 调用 Update 回调
  • CommonUpdate<LateUpdateManager> 调用 LateUpdate 回调
  • CommonUpdate<FixedUpdateManager> 在物理系统已滴答时调用 FixedUpdate

一般来说,BaseBehaviourManager::CommonUpdate<UpdateManager> 是最有用的一类方法来检查,因为它是 Unity 项目中运行的大部分脚本代码的入口点。

还有其他几个方法可供检查

  • 如果某个项目使用了UGUI 系统UI::CanvasManager 会调用几个不同的回调。这包括 Unity 的UI(用户界面)允许用户与你的应用程序进行交互。Unity 目前支持三个 UI 系统。了解更多
    参见术语表
    批处理计算和布局更新,这两个操作最常导致CanvasManagerProfiler(分析器)一个窗口,帮助优化你的游戏。它显示在游戏的各个区域花费了多少时间。例如,它可以报告在渲染、制作动画或在你的游戏逻辑方面花费的时间百分比。了解更多
    参见术语表
    中显示。
  • DelayedCallManager::Update 运行协程
  • PhysicsManager::FixedUpdate 运行 PhysX 物理系统。这主要涉及运行 PhysX 的内部代码。当前场景中的物理对象的数量,例如RigidbodyCollider,会影响 PhysX 的内部代码。基于物理的回调也出现在此处:尤其是 OnTriggerStayOnCollisionStay

如果项目使用的是 2D 物理,那将显示为Physics2DManager::FixedUpdate下的类似调用。

剖析脚本方法

脚本允许你创建你自己的组件的代码,触发游戏事件,构建组件属性,并以你喜欢的任何方式响应用户输入。 了解更多
参见术语表
在交叉编译与IL2CPPUnity 开发的脚本后端,在你为某些平台构建项目时,你可以将它用作 Mono 的替代方案。 了解更多
参见术语表
的平台上被调用时,查找包含 ScriptingInvocation 对象的跟踪行。这是 Unity 的内部原生代码转换为脚本运行时环境来执行脚本代码。注意:从技术上讲,在 Unity 通过 IL2CPP 运行你的 C# 代码之后,它也变为原生代码。但是,此交叉编译代码主要通过 IL2CPP 运行时框架执行方法,而不是类似于手工编写的 C++。

A trace from an example Unity project
来自示例 Unity 项目的跟踪

在上图中,嵌套在 RuntimeInvoker_Void 行下的方法为交叉编译的 C# 脚本的一部分,Unity 会在每帧执行一次。

跟踪行的名称为原始类名称,后跟下划线和原始方法的名称。在此示例跟踪中,你可以看到EventSystem.UpdatePlayerShooting.Update 以及其他几个Update 方法。这些是大多数MonoBehaviours 中常有的标准 Unity Update 回调。

你可以展开这些方法,查看它们中的哪些方法消耗了 CPU 时间。其中包括项目中的其他脚本方法、Unity API 和 C# 库代码。

上面的跟踪显示 StandaloneInputModule.Process 方法每次帧都会对整个 UI 进行一次射线投射。此方法检测是否有任何触摸事件悬停于任何 UI 元素之上或激活任何 UI 元素。在所有 UI 元素上进行迭代并测试鼠标的位置是否在其边界矩形内的方法会消耗大量资源。

资源加载

你还可以识别 CPU 跟踪中的资源加载。指示资源加载的主要方法是 SerializedFile::ReadObject。此方法连接来自文件中的二进制数据流到 Unity 的序列化系统,该系统通过名为 Transfer 的方法进行操作。Transfer 方法存在于所有资源类型,例如纹理、MonoBehaviour 和 粒子系统通过在场景中生成和动画化大量的 2D 图像来模拟液态、云和火焰等流体实体的组件。 更多信息
参见 术语表

Trace of a Scene loading
加载场景的跟踪

上面的屏幕截图是 Unity 加载场景的跟踪。当加载一个场景时,Unity 读取并反序列化场景中的所有资源,这由 SerializedFile::ReadObject 下方的对各种 Transfer 方法的调用表示。

如果你在运行时看到性能抖动,并且性能跟踪显示 SerializedFile::ReadObject 使用了大量时间,则意味着资源加载降低了帧率。注意:SceneManagerResources 或 AssetBundle API 请求同步资源加载时,SerializedFile::ReadObject 通常出现在主线程中。

要解决此性能抖动,你可以使资源加载变成异步(将繁重的 ReadObject 调用移动到工作线程),或者预先加载某些繁重的资源。

在 Unity 克隆对象(在跟踪中由 CloneObject 方法表示)时也会出现 Transfer 调用。如果 Transfer 调用出现在 CloneObject 调用的下方,则 Unity 并没有从存储中加载资源。相反,Unity 将旧对象的数据传输到新对象。为此,Unity 将旧对象序列化,并将生成的数据反序列化为新对象。

分析工具
理解 Unity 中的优化