为每个构建手动沿相同路线玩一遍来测量性能既耗时,又难以在构建之间进行准确比较。 借助开源(Apache-2.0)的 Cluewren,开发者自己的代码编写智能体(Claude Code 或 Codex)可以通过 Cluewren 的 MCP 服务器,直接在 Unity、Godot 或 Unreal Engine 编辑器中游玩已打开的游戏。 智能体阅读代码并编写 C# 测试装置(harness),TypeSafe 快速决策模型 Jev 通过引擎插件执行游玩,最后由智能体结合实测证据报告判定结论。 在此游玩过程中,Framedash Unity SDK 会记录帧时间。 本文通过在两个构建中走完相同路线,找出哪个构建以及关卡的哪个区域变慢了。 候选构建中的性能下降是作为高开销资产或特效的代用品,通过模拟方式引入的。
使用的环境与前置条件
本次验证使用了以下环境:
- Unity:Unity 6000.6.7f1
- 示例项目:安装了 Cluewren Unity 插件的 Unity FPS Microgame(MainScene)项目副本
- Cluewren 运行要求:Windows 11(已测试)、.NET 10 SDK、从源码构建、TypeSafe API 密钥、Claude Code 或 Codex
Cluewren 本身不向 Cluewren 服务器发送任何数据。 游戏过程中的决策请求发送给 TypeSafe 的 API,智能体则与各自的服务提供商通信。
安装插件与 SDK
Cluewren 本身的安装步骤见 Cluewren 安装指南,向 Unity 项目添加插件见 Cluewren Unity 插件文档。 Framedash Unity SDK v0.1.8 通过 Unity 的 Package Manager git URL 添加:
https://github.com/crane-valley/framedash-unity-sdk.git#v0.1.8
添加遥测探针脚本
向项目中添加以下脚本:
using System;
using Framedash;
using Unity.FPS.Gameplay;
using UnityEngine;
public sealed class FramedashPlaytestProbe : MonoBehaviour
{
const string MapId = "fps-microgame-main";
const float Interval = 0.25f;
PlayerCharacterController _player;
float _next;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void Boot()
{
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FRAMEDASH_API_KEY")))
return;
TelemetrySDK.Initialize(buildId: Environment.GetEnvironmentVariable("FRAMEDASH_BUILD_ID"), enableOfflineQueue: false);
TelemetrySDK.Instance.BeginAutomatedSessionFromEnvironment();
var go = new GameObject(nameof(FramedashPlaytestProbe));
DontDestroyOnLoad(go);
go.AddComponent<FramedashPlaytestProbe>();
}
void Update()
{
if (Time.unscaledTime < _next)
return;
_next = Time.unscaledTime + Interval;
if (_player == null)
_player = FindAnyObjectByType<PlayerCharacterController>();
if (_player != null)
TelemetrySDK.Instance.Track("player_position", MapId, _player.transform.position);
}
void OnApplicationQuit() => TelemetrySDK.Instance.FlushBlocking(5000);
}
该脚本仅在设置了 FRAMEDASH_API_KEY 时才会初始化 SDK。
构建标签会根据 FRAMEDASH_BUILD_ID 与 FRAMEDASH_TEST_SCENARIO 自动设置。
玩家坐标每 0.25 秒发送一次,作为 map_id 为 fps-microgame-main 的 player_position 事件。
根据 SDK 的机制,每个 Track() 事件都会携带当时的帧时间、FPS、GPU 时间和内存数据,因此位置样本同时也充当了各位置的帧时间样本。
此外,SDK 每 10 秒会自动发送一次 perf_heartbeat。
设置环境变量并启动编辑器
启动 Unity 编辑器前,设置以下环境变量:
FRAMEDASH_API_KEY=<具有 events:write 权限的 Ingest 密钥>
FRAMEDASH_BUILD_ID=fps-walk-base
FRAMEDASH_TEST_SCENARIO=cluewren-walkthrough
智能体编写并修正测试装置的首次运行需要使用专属的构建 ID(本次使用了 fps-walkthrough-baseline),以使其事件不参与后续比较。
随后的实际测量中,在设置对应环境变量后分别重新启动编辑器,基线运行指定为 fps-walk-base,候选构建运行指定为 fps-walk-heavy-chamber。
API 密钥只能设置在运行编辑器的环境中,切勿粘贴到智能体对话中。
向智能体下达指令与首次冷运行
对两个构建都向代码编写智能体(Claude Code、Opus)下达完全相同的指令:
Walk the player through the whole level: start at the spawn point, go through every area the level contains, and finish back at the start. Do not fight unless an enemy blocks the way.
(控制玩家巡游整个关卡:从出生点出发,穿过关卡包含的每个区域,最后返回起点。除非有敌人挡住去路,否则不要战斗。)
在首次(冷)运行中,智能体消耗了 3 个回合(episodes)和 471 秒。
在第 1 回合中,它从场地边缘掉进了熔岩;在第 2 回合中,它被 HoverBot 击杀。
智能体每次都修正了自己的测试装置,并在第 3 回合走完了全程。
由于本次运行记录在专属的构建 ID fps-walkthrough-baseline 之下,因此其事件不会包含在比较中。
生成的测试装置会保存在项目的运行内存中,并在遇到相同请求时复用。
两个构建的运行结果
候选构建在下层密室(lower chamber)中模拟了高开销效果:当玩家位于密室内时,主线程每帧自旋(spin)12 ms。 这是对实际繁重资产或特效负载的替代模拟。
由于复用了预热好的测试装置,用于比较的两次运行各仅耗费了 1 个回合:
- 基线(
fps-walk-base):达成目标(achieved),报告耗时 84.7 秒,在 t=62.0 秒时回到出生点。巡游路线依次为中央大厅、西侧祭坛、大开阔场地的 4 个象限、场地门道、穿过大门的阶梯、下层密室入口·尽头·东侧,最后回到出生点。战斗仅发生 1 次(击杀向其射击的 HoverBot,耗时 2.8 秒)。 - 候选构建(
fps-walk-heavy-chamber):达成目标(achieved),报告耗时 83.4 秒,按相同顺序执行了相同的 12 个计划步骤,在 t=62.4 秒时回到出生点。同样执行了 1 次战斗(2.8 秒)。
用 perf-diff 与 Regression 页面进行比较
使用具有 analytics:read 权限的密钥,通过 Framedash CLI(@framedash/cli 0.2.0)的 perf-diff 命令比较两个构建:
npx @framedash/cli perf-diff --baseline fps-walk-base --candidate fps-walk-heavy-chamber --metric frame_time --threshold 10 --days 7
各指标的输出结果如下:
frame_time:基线 P50 为 8.92 ms,候选构建 P50 为 15.09 ms(+69.3 %,isRegression为true);P95 从 52.66 ms 变为 53.20 ms。memory:-0.4 %(未发生性能回归)。gpu_time:-15.6 %(未发生性能回归;绝对值仅在 1.4~1.6 ms 左右,属于测量噪声)。
输出中的 isRegression 标志仅表示方向:只要指标变差即为 true。
该标志不会应用百分比阈值。
--threshold 仅在传入 --fail-on-regression 时用于控制退出码。
若想在回归超过阈值时让 CI 作业失败,请加上 --fail-on-regression。
有关 CI 配置与流程,请参阅在合并前,用 CI 比较游戏性能、在 CI 中检测 Unity 性能回归以及 CLI 参考。
控制台的 Regression 页面也显示了相同的结果:
控制台上的数值与 CLI 一致:Frame Time 增加了 +69.3 %(从 8.92 ms 升至 15.09 ms),两个构建各采集了 299 个样本。
通过查询按区域细分分析
为了找出性能下降发生的位置,使用 CLI 的 query 命令执行 SQL 按区域分组聚合。
该命令需要具有 data:admin 权限的 Full 密钥(只读密钥会收到 403 错误)。
下层密室是指 Unity 世界坐标中低于地平面(y < -3)且 x > 20 的区域。
npx @framedash/cli query "SELECT build_id, (position_y < -3 AND position_x > 20) AS lower_chamber, count() c, round(quantile(0.5)(frame_time_ms),1) p50, round(quantile(0.95)(frame_time_ms),1) p95 FROM events WHERE build_id IN ('fps-walk-base','fps-walk-heavy-chamber') AND event_name='player_position' GROUP BY build_id, lower_chamber ORDER BY build_id, lower_chamber"
聚合结果如下:
| build_id | 区域 | 样本数 | P50 | P95 |
|---|---|---|---|---|
| fps-walk-base | 下层密室外 | 227 | 7.6 ms | 52.9 ms |
| fps-walk-base | 下层密室 | 65 | 11.5 ms | 36.7 ms |
| fps-walk-heavy-chamber | 下层密室外 | 224 | 7.0 ms | 53.2 ms |
| fps-walk-heavy-chamber | 下层密室 | 68 | 26.0 ms | 54.0 ms |
结果表明,全部性能回归都集中在下层密室内(P50 增加 +14.5 ms,与每帧 12 ms 的自旋及开销吻合)。 路线的其余部分帧时间没有变化。
测量时的注意事项
在应用该流程时,请注意以下几点:
- 编辑器与画面捕获开销:本次测量在 Unity 编辑器的 Play 模式下运行,同时 Cluewren 为智能体捕获画面帧。两个构建的 P95 都在 53 ms 左右,正是由编辑器与画面捕获开销导致的,平台记录为 WindowsEditor。该测量应用作相同条件下构建间的相对比较,不能作为面向真实玩家的绝对性能指标。有关独立玩家构建的测量,请参阅用接近发布的构建验证优化。
- 保持运行条件一致:两个构建应使用相同的指令、相同的机器和相同的预热测试装置,以保证测试条件严格一致。
- 密钥管理:Ingest 密钥只能配置在启动游戏的环境变量中,切勿粘贴到智能体对话中(这也是 Cluewren 对其自身 TypeSafe 密钥所遵循的规则)。
总结
智能体在两个构建中执行相同的指令,Framedash 负责记录帧时间。
通过 perf-diff 命令与控制台 Regression 页面可以确认哪个构建变慢了,对位置样本执行查询则能精确定位关卡中变慢的区域。
了解 Cluewren 的更多信息,请访问 Cluewren 官网。