Unity中的性能基准测试:开始使用

作为Unity开发人员,您希望用户喜欢玩您的游戏并在他们可能使用的所有平台上享受流畅的体验。如果我告诉您我们刚刚使创建性能基准变得更容易了呢?如果您想了解如何开发游戏或Unity工具并注重性能,请继续阅读!
在这篇文章中,我将解释如何使用一些Unity工具,这些工具让您可以轻松地开始收集性能指标并用它们创建基准: Unity编辑器附带的 Unity Test Runner、Unity性能测试扩展和 Unity性能基准报告器。
作为Unity开发人员,您可能会遇到以下情况:您的项目不久前运行快速且流畅,但后来出现了一个或多个更改,现在场景明显变慢、帧数下降,并且开始出现其他性能问题。追踪哪些变化导致了性能下降可能很困难。
如果您是Unity合作伙伴,您希望了解 SDK、驱动程序、平台、软件包或其他工件的性能变化。或者您想收集产品中不同Unity版本的性能指标,但不太清楚如何执行此操作并进行比较。
这只是建立性能基准确实可以挽救局面的几个例子。现在,让我向您展示如何开始收集性能指标、用它们创建基准以及可视化性能指标的变化。

为了进行本次讨论,我们将查看 UnityPerformanceBenchmark 示例性能测试项目中的测试代码。
从GitHub下载 最新的 XRAutomatedTests 版本 。您会在 PerformanceTests 子目录中找到 UnityPerformanceBenchmark 项目。
UnityPerformanceBenchmark 项目包含各种示例场景,这些场景又可通过Unity性能测试扩展用于Unity性能测试。
我们要做的第一件事是看看如何使用带有 Unity性能测试扩展的Unity Test Runner 编写性能测试。在我们继续之前,这里先介绍一下这两种工具的一些背景信息。
我们正在使用Unity Test Runner 来运行我们的性能测试。Unity Test Runner 是Unity Editor 内置的测试执行框架,可让您在目标平台播放器(例如 Standalone、Android 或 iOS)上以编辑和播放模式测试代码。如果您不熟悉Unity Test Runner,请查看 Unity Test Runner 文档。
Unity性能测试扩展 是一个Unity编辑器 包 ,它提供了API和测试用例属性,让您可以在Unity编辑器和播放器中采样和聚合Unity分析器标记和非分析器自定义指标。您可以通过查看 Unity性能测试扩展文档了解更多信息,但在这里我们将看一些示例。
Unity性能测试扩展需要Unity2018.1 或更高版本。如果您想在 UnityPerformanceBenchmark 项目中运行示例性能测试或使用Unity性能测试扩展,请务必使用Unity版本 2018.1 或更高版本。
UnityPerformanceBenchmark 项目实现了 IPrebuildSetup 接口,这是一个Unity Test Runner 工具,我们可以在其中实现一个 Setup 方法,该方法在Unity Test Runner 执行测试运行之前自动调用。
UnityPerformanceBenchmark 项目的 IPrebuildSetup.Setup 方法所做的第一件事是解析命令行参数以查找玩家构建设置。这样,我们就可以灵活地使用同一个Unity项目针对不同的平台、渲染线程模式、播放器图形 API、脚本实现以及支持XR的设置(例如立体渲染路径和VR SDK)构建播放器以进行性能测试。
因此,我们需要从命令行使用Unity打开 UnityPerformanceBenchmark 项目,并传入我们在Unity Test Runner 中运行测试时想要使用的播放器构建选项。
例子:从 Windows 启动 UnityPerformanceBenchmark 项目来构建 Android 播放器:
Unity.exe -projectPath
C:\XRAutomatedTests-2018.2\PerformanceTests\UnityPerformanceBenchmark
-testPlatform Android -buildTarget Android -playergraphicsapi=OpenGLES3 -mtRendering -scriptingbackend=mono
在这里,我们在 Windows 上启动Unity ,使用 OpenGLES3 图形API、多线程渲染和单声道脚本后端为 Android 构建。
例子:从 OSX 启动 UnityPerformanceBenchmark 项目以构建 iOS 播放器
./Unity -projectPath /XRAutomatedTests-2018.2/PerformanceTests/UnityPerformanceBenchmark
-testPlatform iOS -buildTarget iOS -playergraphicsapi=OpenGLES3 -mtRendering -scriptingbackend=mono
-appleDeveloperTeamID=<yourAppleDeveloperTeamID>
-iOSProvisioningProfileID=<yourIosProvisionProfileID>
在这里,我们在 OSX 上启动Unity ,使用 OpenGLES3 图形API、多线程渲染和单声道脚本后端为 iOS 构建。我们还提供部署到 iOS 设备所需的 Apple 开发团队和配置文件信息。
当我们像上面的示例一样从命令行使用Unity打开 UnityPerformanceBenchmark 项目时,命令行参数将存在于内存中,以供 IPrebuildSetup.Setup 方法解析并用于构建播放器。
虽然这种从命令行启动的方法不是在Unity Test Runner 中运行测试所必需的,但它是一种很好的模式,可以避免为每个播放器配置使用单独的测试项目。
我已经详细介绍了从测试项目的 wiki 上的命令行打开项目或仅运行测试的命令行选项:如何运行Unity性能基准测试。要了解有关我们如何在测试项目中解析播放器构建设置的更多信息,请查看 UnityPerformanceBenchmark 测试项目的 Scripts 目录中的 RenderPerformancePrebuildStep.cs 文件。
打开 UnityPerformanceBenchmark 后,我们需要在Unity Editor 中打开Unity Test Runner 窗口
- 在Unity 2018.1 中,转到 Window > Test Runner。
- 在Unity 2018.2 中,转到 Window > General > Test Runner。
Unity Test Runner 窗口将打开并如下图所示。

这些是我们的Unity性能测试。我们可以使用窗口左上角的“运行”按钮在编辑器中运行它们,或者使用窗口右上角的“在播放器中全部运行”按钮在实际设备或平台上运行它们。
调试提示
如果你想在 IPrebuildSetup.Setup 方法中调试代码
1.在 Visual Studio 中的 IPrebuildSetup.Setup 代码中设置断点
2.使用 Visual Studio Tool for Unity扩展附加到Unity编辑器
3.使用Unity Test Runner 窗口中的“运行全部”或“运行选择”按钮在编辑器中运行测试。
此时,Visual Studio 调试器将中断您的代码,您可以根据需要进行调试。
让我们看一个性能测试示例,以便更好地理解它是如何工作的。
例子:在Unity性能测试中对分析器标记进行采样
[PerformanceUnityTest]
public IEnumerator SpiralFlame_RenderPerformance()
{
yield return SceneManager
.LoadSceneAsync(spiralSceneName, LoadSceneMode.Additive);
SetActiveScene(spiralSceneName);
// Instantiate performance test object in scene
var renderPerformanceTest =
SetupPerfTest<DynamicRenderPerformanceMonoBehaviourTest>();
// allow time to settle before taking measurements
yield return new WaitForSecondsRealtime(SettleTime);
// use ProfilerMarkers API from Performance Test Extension
using (Measure.ProfilerMarkers(SamplerNames))
{
// Set CaptureMetrics flag to TRUE;
// let's start capturing metrics
renderPerformanceTest.component.CaptureMetrics = true;
// Run the MonoBehaviour Test
yield return renderPerformanceTest;
}
yield return SceneManager.UnloadSceneAsync(spiralSceneName);
}
在这个例子中,我们的测试方法被称为 SpiralFlame_RenderPerformance。我们从方法装饰器[PerformanceUnityTest]知道,这是一个Unity性能测试。
UnityPerformanceBenchmark 测试项目中的所有测试都遵循我们在该测试方法中看到的相同模式:
1.加载测试场景
2.将场景设置为活动状态,以便我们可以在测试方法中与其进行交互
3.创建 DynamicRenderPerformanceMonoBehaviourTest 类型的测试对象并将其添加到测试场景(这发生在 SetupPerfTest<T> 方法中)
4.在加载并将测试对象添加到场景后,等待场景“稳定”一个恒定的时间值,然后再开始采样指标。
5.设置我们的分析器标记,以便性能测试扩展API进行捕获
6.让性能测试知道我们已准备好开始捕获指标
7.然后yield测试对象(IMonoBehaviourTest)来捕获渲染循环期间的指标。
我们还在 RenderPerformanceMonoBehaviourTestBase 基类(此类继承自MonoBehaviour )中对自定义指标(不属于Unity分析器标记、帧数或执行时间之一的指标)进行采样。
例子:在 Monobehaviour 脚本中采样自定义指标
private void Update()
{
if (CaptureMetrics)
{
FrameCount++;
SampleFps();
#if ENABLE_VR
if (XRSettings.enabled)
{
SampleGpuTimeLastFrame();
}
#endif
}
if (IsMetricsCaptured)
{
EndMetricCapture();
}
}
private void SampleFps()
{
Fps = GetFps();
Measure.Custom(FpsSg, Fps);
startFrameCount = Time.renderedFrameCount;
}
private void SampleGpuTimeLastFrame()
{
var gpuTimeLastFrame = GetGpuTimeLastFrame();
Measure.Custom(GpuTimeLastFrameSg, gpuTimeLastFrame * 1000);
}
public void EndMetricCapture()
{
CaptureMetrics = false;
#if UNITY_ANALYTICS && UNITY_2018_2_OR_NEWER
Measure.Custom(startupTimeSg, appStartupTime);
#endif
}
在上面的示例中,我们捕获了FPS、GpuTimeLastFrame(如果启用了XR )和应用程序启动时间(如果启用了Unity Analytics ,并且我们在Unity 2018.2 或更新版本上运行,其中需要的API可用)。
最后,请注意在同一个RenderPerformanceMonoBehaviourTestBase基类中我们已经实现了一个属性public bool IsTestFinished。我们需要实现这个属性因为我们的RenderPerformanceMonoBehaviourTestBase实现了 IMonoBehaviourTest 接口。
此属性很重要,因为Unity Test Runner 使用它来知道何时停止测试。当其值为真时,测试结束。您可以自行实现所需的逻辑来确定Unity Test Runner 何时停止运行测试。
例子:在 IsTestFinished 属性中采样自定义指标
public bool IsTestFinished
{
get
{
bool isTestFinished = false;
if (IsMetricsCaptured)
{
Measure.Custom(objCountSg, RenderedGameObjects);
Measure.Custom(trianglesSg, Tris);
Measure.Custom(verticesSg, Verts);
isTestFinished = true;
}
return isTestFinished;
}
}
在这个最后的例子中,我们在测试完成时捕获了场景中渲染的游戏对象、三角形和顶点的数量。
现在我们已经看到了一些如何调用性能测试扩展来采样指标的示例,让我们开始讨论如何配置它们。
Measure.* 方法通常将一个称为 SampleGroupDefinition 的结构作为参数。当我们创建新的 SampleGroupDefinition 时,我们会为我们感兴趣的收集的样本定义一些属性。
例子:为 GpuTimeLastFrame 定义新的 SampleGroupDefinition,以毫秒为样本单位,使用最小值聚合样本
以下是 GpuTimeLastFrame 的 SampleGroupDefinition。这就是我们让性能测试扩展知道如何收集样本并为 GpuTimeLastFrame 聚合它们的方式。
此 SampleGroupDefinition 来自动态场景渲染性能测试示例,因此我们在此选择使用收集到的最小值来聚合我们的样本。但是为什么我们要这样做,而不是使用更常见的聚合度量,例如中位数或平均值?
答案是因为场景是动态的。在动态场景中,考虑到渲染的变化性质,对于针对相同代码运行的相同场景,使用中值或平均聚合是不可靠或不一致的。如果我们想要跟踪动态场景中渲染指标的单个聚合,这很可能是我们能做的最好的事情。但是,当我们为静态场景定义类似的 SampleGroupDefinition 时,我们肯定会使用中值聚合。
new SampleGroupDefinition(GpuTimeLastFrameName, SampleUnit.Millisecond, AggregationType.Min)
例子:为FPS定义新的 SampleGroupDefinition ,使用 none 作为样本单位,使用中值聚合样本,该值越大越好
以下是FPS (每秒帧数)的 SampleGroupDefinition。FPS没有单独的测量单位;它只是FPS,所以我们在这里指定 SampleUnit.None。我们将在这里使用中值聚合类型;这是在静态场景中,因此我们不必担心不可预测的渲染体验。我们明确为样本组设立了 15% 的阈值,并将 increaseIsBetter 参数设置为 true,因为如果FPS增加,那么这是一件好事!
从命令行运行时,最后两个参数会被收集并保存在我们的性能测试结果 .xml 文件中,稍后可在Unity Performance Benchmark Reporter 中用来建立基准。
新的SampleGroupDefinition(FpsName,SampleUnit.None,AggregationType.Median,阈值:0.15,increaseIsBetter:true)
当测试完成时,我们之前启用的所有指标样本都会由性能测试扩展进行汇总。
我想指出的是,在我们的代码示例中,我们使用了几个不同的Unity性能测试扩展 API,即
- Measure.ProfilerMarkers,以及
- 测量.自定义
Unity性能测试扩展还提供了其他测量方法,这些方法可能适合您的特定需求,具体取决于您想要在Unity中测量性能的内容和方式。这些额外的方法包括:
- 测量方法
- Measure.Frames
- 测量.范围
- Measure.FrameTimes
在 Unity性能测试扩展文档中了解有关不同测量方法的更多信息,特别是在“进行测量”部分。
现在我们已经看了一些如何使用Unity性能测试扩展的Unity Test Runner 编写性能测试的示例,让我们看看如何运行它们。
执行性能测试的主要方法有两种
1.从命令行,使用 -runTests 选项启动Unity 。这是性能测试的首选方式,因为Unity Performance Test Extension 将为我们生成一个结果 .xml 文件,我们可以在Unity Performance Benchmark Reporter 中使用该文件来查看和比较我们的结果。
2.直接从编辑器内部。如果您
a.只想运行测试并在Unity Test Runner 窗口中查看结果,而无需捕获结果以供日后使用,或者
b.想要验证您的测试是否能够运行或者您需要调试测试代码。
以下是两个如何从命令行使用Unity Test Runner 运行性能测试的示例。这些示例看起来应该非常熟悉,因为我们是基于之前在讨论从命令行打开 UnityPerformanceBenchmark 项目中看到的相同示例构建的。
例子:在 Windows 中针对 Android 玩家运行 UnityPerformanceBenchmark 性能测试
在这里,我们在 Windows 上启动Unity ,使用 OpenGLES3 图形API、多线程渲染和单声道脚本后端为 Android 构建。
Unity.exe -runTests [-batchmode] -projectPath
C:\XRAutomatedTests-2018.2\PerformanceTests\UnityPerformanceBenchmark-testPlatform Android-buildTarget Android-playergraphicsapi=OpenGLES3-mtRendering-scriptingbackend=mono-testResults
C:\PerfTests\results\PerfBenchmark_Android_OpenGLES3_MtRendering_Mono.xml -logfile
C:\PerfTests\logs\PerfBenchmark_Android_OpenGLES3_MtRendering_Mono_UnityLog.txt
例子:从 OSX 针对 iOS 播放器运行 UnityPerformanceBenchmark 性能测试
在这里,我们在 OSX 上启动Unity ,使用 OpenGLES3 图形API、多线程渲染和单声道脚本后端为 iOS 构建。我们还提供部署到 iOS 设备所需的 Apple 开发团队和配置文件信息。
./ Unity -runTests [-batchmode] -projectPath /XRAutomatedTests-2018.2/PerformanceTests/UnityPerformanceBenchmark
-testPlatform iOS -buildTarget iOS -playergraphicsapi=OpenGLES3
-mtRendering -scriptingbackend=mono
-appleDeveloperTeamID=<yourAppleDeveloperTeamID>
-iOSProvisioningProfileID=<你的 iOSProvisionProfileID> -testResults /PerfTests/results/PerfBenchmark_Android_OpenGLES3_MtRendering_Mono.xml
-logfile /PerfTests/logs/PerfBenchmark_Android_OpenGLES3_MtRendering_Mono_UnityLog.txt
对于这两个示例,我们引入了三到四个新的命令行选项,它们将帮助我们运行测试,而不仅仅是使用 IPrebuildSetup.Setup 方法可用的命令行参数打开Unity编辑器。
-runTests
此选项告诉Unity Test Runner 你想要运行测试
-testResults <pathToWritePerformanceTestResultsFile>
此选项指定Unity Test Runner 应保存性能测试结果的 .xml 文件的文件名和路径。
-logfile <pathToWriteUnityEditorLogFile>
此选项指定Unity编辑器应将其日志写入的文件的文件名和路径。这是可选的,但如果您可以快速访问Unity编辑器日志文件,那么在调查故障和问题时会非常有用。
-batchmode
此选项将强制Unity编辑器以无头模式打开。当我们仅运行玩家性能测试且无需实际打开Unity编辑器窗口时,我们会使用此选项。这可以在自动测试执行期间节省时间。当不使用此选项时, Unity编辑器将在执行测试之前在屏幕上打开。
在Unity,我们在持续集成系统中从命令行运行性能测试,通常以批处理模式运行。
例子:从命令行运行 UnityPerformanceBenchmark 测试

当选择 PlayMode 时, Unity Test Runner 窗口在顶部附近打开(PlayMode 测试在构建播放器或编辑器的播放模式窗口中运行),我们有
1.全部运行 - 单击此按钮可运行 PlayMode 选项卡中的所有测试
2.运行选定项 - 单击此按钮可运行选定的测试或节点及其下方的所有测试。
3.在播放器中运行全部 - 单击此项可让Unity编辑器构建在构建设置中配置的播放器类型并在那里运行测试
重要要求从测试运行器窗口运行Unity编辑器中性能测试扩展 0.1.50 版本之前的性能测试将不会生成Unity性能基准报告器所需的结果 .xml 文件。但是,如果您使用的是性能测试扩展 0.1.50 或更高版本,则 results.xml 文件将被写入“Assets\StreamingAssets”项目文件夹。
如果您使用的是早于 0.1.50 版本的性能测试扩展,并且想要在完成性能测试后创建结果 .xml 文件,则需要通过使用 -runTests 命令行选项从命令行启动Unity来运行测试。但请注意,当您使用 -runTests 命令行选项运行Unity时,编辑器将打开并开始运行测试。
结果 .xml 文件将包含测试运行的结果和元数据,我们将使用Unity Performance Benchmark Reporter 创建基准测试结果并与后续测试运行进行比较。
例子:在Unity编辑器中运行性能测试

如果我们在编辑器中运行这些测试,则通过选择每个测试可以在Unity Test Runner 窗口的底部附近看到聚合值。
例子:从Unity Test Runner 查看性能测试示例聚合

如果您想从命令行查看运行Unity性能测试的结果,则需要使用Unity性能基准报告器(或者只需打开结果 .xml 文件,但读取起来并不容易)。
接下来,让我们讨论如何使用Unity Performance Benchmark Reporter 查看和比较结果。
Unity Performance Benchmark Reporter 支持在带有图形可视化的 HTML 报告中比较性能指标基线和后续性能指标(使用 Unity Test Runner 和 Unity Performance Testing Extension生成)。
该报告器以 .NET Core 2.x 程序集的形式构建,因此可以兼容在不同的 .NET 支持平台(Windows、OSX 等)上运行。因此,要运行它,您需要确保已安装 .NET Core SDK。
执行Unity性能基准测试报告需要使用 dotnet 命令调用程序集,如下所示:
dotnet UnityPerformanceBenchmarkReporter.dll
--baseline=D:\UnityPerf\baseline.xml
--results=D:\UnityPerf\results --reportdirpath=d:\UnityPerf
报告程序运行后,将创建一个名为 UnityPerformanceBenchmark 的目录,其中包含一个 html 报告以及支持的 .css、.js 和图像文件。打开 html 报告以查看 .xml 结果文件中捕获的性能指标可视化效果。
--results
目录的路径,其中我们有一个或多个非基线结果 .xml 文件要包含在 html 报告中。
必须将至少一个 --results 值传递给 UnityPerformanceBenchmarkReporter.dll 程序集。这是唯一必填字段。
此命令行选项还可用于指定单个.xml 非基线结果文件的路径。此外,您可以通过重复该选项来指定多个目录或文件,如下所示:
--results=D:\UnityPerf\results --results=D:\UnityPerf\results.xml
--baseline
比较其他结果时将使用的结果 .xml 文件的路径。
--reportdirpath
报告者将创建性能基准报告的目录的路径。这是在 UnityPerformanceBenchmark 子目录中创建的。
如果未指定报告位置,则会在调用 UnityPerformanceBenchmarkReporter.dll 的工作目录中创建 UnityPerformanceBenchmark 子目录。
让我们将一些性能测试结果与性能基准报告器进行比较。
例子:在支持VR的Gear VR场景中尝试更改配置以提高帧速率
我有一个具有以下复杂性特征的Unity场景。
- 732 个对象
- 95,898 个三角形
- 69,740 个顶点

我针对此场景采样指标运行了Unity性能测试,这将有助于我了解是否可以使用多通道立体渲染维持接近 60 FPS 。接下来,我运行了性能基准测试报告并得到了测试结果。
我发现我的FPS更接近 30 FPS,是我想要的一半。

接下来,我将尝试使用单通道多视图立体渲染来查看我能获得多接近 60 FPS 的效果。我将使用配置更改重新运行我的性能测试,然后创建另一个Unity性能基准测试报告,将我的第一个结果与新的结果进行比较。

看起来,切换到单通道多视图渲染将我们的FPS提高到了 37。如果我们希望这个场景在Gear VR上运行时不出现明显的帧丢失,我们仍然需要更接近 60 FPS 。
我要尝试的最后一件事是减少场景中旋转立方体的数量,看看是否可以提高FPS 。
经过几次尝试,我能够将性能提高到~55 FPS。但我必须将场景中的物体数量从 732 个减少到 31 个。这相当多。
我将回顾可以进行的其他性能优化改进,但现在,我将使用它作为FPS基准。我会以此作为我今后的基准,希望能够有所改进。

根据您的项目,建立基准可能意味着很多事情。在这种情况下,在Unity中运行性能测试,我们正在讨论建立一组基线结果,即一组最后已知的良好性能指标,我们可以在进行更改时将后续结果与之进行比较。这些成为我们的基准。
在上一节中,我得到了一个针对Gear VR 的单通道多视图立体渲染配置,并减少了场景对象数量,从而获得了“可接受的” FPS。此时,我决定以我的测试结果作为我的基准。让我们看一个例子,了解如何在对播放器配置进行进一步更改时使用这个基准。
例子:使用性能基准来检测配置更改导致的性能下降
我想在场景中启用抗锯齿功能以使外观更加平滑。Unity for Android 中的默认质量设置禁用抗锯齿,但我想看看我们是否可以启用它,并且仍为我们的Gear VR场景保持可接受的FPS 。
首先,我将 IPrebuildSetup.Setup 方法中的抗锯齿值设置为 4。
QualitySettings.antiAliasing = 4;
接下来,我在支持Gear VR的 Android 手机上重新运行了之前的性能测试。然后,我使用Unity Performance Benchmark Reporter 将这次新运行与我新建立的基准测试结果进行比较。

但是,看看吧,将我的Unity播放器重新配置为使用 4 级抗锯齿后,我的FPS下降到了 32 FPS,这与我最初创建这个包含 732 个对象的场景时的 FPS 差不多。
在放弃这个想法之前,我想尝试一些较低的抗锯齿值,看看是否可以恢复场景可接受的FPS 。因此,我尝试将抗锯齿设置为 2,最后设置为 1。结果如下图所示。

在这种重新配置场景中,使用我之前建立的性能基准,我能够尝试更改Unity播放器设置,然后在实施之前验证性能影响。
尽管我将抗锯齿设置为 1,并将其置于默认的 15% FPS方差阈值之内,但现在的FPS仅为 49,与我想要的VR场景的 60 FPS相差甚远。我想我今天不会做出这些改变。
Unity 默认非常注重出色的性能。但Unity引擎只是最终让用户喜爱玩您的游戏、在所有平台上享受流畅和高性能体验的一部分。例如,SDK、驱动程序或Unity软件包如果能够在不引入性能回归的情况下顺利运行,那么对于每个人获得整体出色的性能体验就至关重要。
我向您介绍了一些Unity工具,让您可以更轻松地开始收集性能指标并用它们创建基准: Unity性能测试扩展和Unity性能基准报告器。我鼓励您尝试一下它们能为您和您的以绩效为中心的努力做些什么。
我们研究了
- 如何使用Unity Test Runner 编写性能测试来采样分析器和其他指标,
- 我们可以使用Unity Test Runner 执行性能测试的一些不同方法,以及
- 在开始提高性能测试水平时,如何使用Unity性能基准报告器来分析和比较性能指标,并不断运行。
为这些指标建立基线,并使用它们为您的场景、游戏、SDK、驱动程序、包或其他Unity集成创建基准,可以有效地开始了解您的更改的影响。祝你好运!
非常感谢我的Unity同事,感谢他们与我一起为这项工作做出贡献、集思广益、进行实验、开发和迭代。
- 齐江
- Sakari Pitkänen
- 金塔乌塔斯·斯克尔西斯
- 本杰明·史密斯