UE5 SDK
本文介绍如何使用 Framedash UE5 SDK(C++ 插件)自动收集性能遥测数据。
- Unreal Engine 5.3 及以上
- 纯 Blueprint 项目可使用匹配的预编译包;从源码构建需要 C++ 项目和工具链
对于纯 Blueprint 项目,请使用 Fab 页面或 GitHub Releases 页面上的预编译包。请选择与你的引擎和目标平台匹配的包。GitHub 为 UE 5.3-5.8 提供 framedash-ue5-v<version>-ue<engine>.zip(例如 UE 5.6 使用 -ue5.6),其中包含已编译的 Win64 Binaries,因此使用 Launcher 引擎的项目无需重新构建即可使用。
若要从源码构建,或面向预编译包未覆盖的平台,请改为克隆公开镜像。此路径需要 C++ 项目和工具链。源码中的 Framedash.uplugin 未固定引擎版本,因此可在 Framedash.Build.cs 支持的所有平台(Win64 / Mac / iOS / Android / Unix)上构建:
git clone https://github.com/crane-valley/framedash-ue5-sdk.git然后将其添加到项目中:
- 将插件放置到
Plugins/Framedash目录 - 在
.uproject中添加:
{ "Plugins": [ { "Name": "Framedash", "Enabled": true } ]}- 如果项目包含 C++ 模块并从 C++ 调用插件,请在该模块的
Build.cs中添加依赖:
PrivateDependencyModuleNames.Add("Framedash");- 使用源码插件、源码构建的引擎或预编译包未覆盖的目标时,重新构建项目
方法 A:自动初始化(推荐)
Section titled “方法 A:自动初始化(推荐)”在 DefaultGame.ini 中添加以下内容,子系统将在启动时自动初始化:
[/Script/Framedash.FramedashSettings]ApiKey=your-api-keybAutoInitialize=True也可以设置可选字段,如 BuildId、SamplingRate 和 PlayerId:
[/Script/Framedash.FramedashSettings]ApiKey=your-api-keybAutoInitialize=TrueBuildId=1.0.0SamplingRate=1.0PlayerId=player-123PlayerId 是开发者提供的玩家标识符。若留空,事件将以匿名方式发送,且 SDK 会记录 No player_id set. Events will be sent as anonymous.。
编译后的默认 EndpointUrl 已指向 https://ingest.framedash.dev/v1/events,因此仅在使用本地或自托管的 ingest 端点时才需要设置。若要设置,请用双引号包裹该值:
EndpointUrl="https://ingest.framedash.dev/v1/events"这些设置也可以在 Project Settings > Plugins > Framedash 中编辑。
无需编写 C++ 初始化代码。
方法 B:通过 C++ 手动初始化
Section titled “方法 B:通过 C++ 手动初始化”您也可以不使用配置文件,直接通过代码初始化:
#include "FramedashSubsystem.h"
void AMyGameMode::BeginPlay(){ Super::BeginPlay();
if (auto* Subsystem = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()) { FString ApiKey = FPlatformMisc::GetEnvironmentVariable(TEXT("FRAMEDASH_API_KEY")); Subsystem->InitializeTelemetry(ApiKey); }}InitializeTelemetry 还接受可选的 EndpointUrl 和 BuildId 参数,在 CI 环境中很有用:
if (auto* Subsystem = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ FString ApiKey = FPlatformMisc::GetEnvironmentVariable(TEXT("FRAMEDASH_API_KEY")); FString BuildId = FPlatformMisc::GetEnvironmentVariable(TEXT("FRAMEDASH_BUILD_ID")); // 传递空字符串作为 EndpointUrl 以使用默认值 Subsystem->InitializeTelemetry(ApiKey, TEXT(""), BuildId);}自动收集的数据
Section titled “自动收集的数据”初始化完成后,以下数据将自动收集:
- FPS / Frame Time: 等同于
stat unit的数据 - GPU Time: RHI 可用时报告的 GPU 帧时间
- Memory: 平台报告的已用物理内存
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ const FVector PlayerLocation(1000.0f, 2000.0f, 50.0f); Framedash->Track(TEXT("player_death"), TEXT("Map01"), PlayerLocation);}携带自定义属性和指标
Section titled “携带自定义属性和指标”需要附加额外元数据时,使用 TrackWithData:
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ const FVector PlayerLocation(1000.0f, 2000.0f, 50.0f);
TMap<FString, FString> Attributes; Attributes.Add(TEXT("cause"), TEXT("fall_damage"));
TMap<FString, double> Metrics; Metrics.Add(TEXT("health"), 0.0);
Framedash->TrackWithData( TEXT("player_death"), TEXT("Map01"), PlayerLocation, Attributes, Metrics);}运行时采样覆盖
Section titled “运行时采样覆盖”全局 SamplingRate(项目设置)应用于所有 Player 来源的事件;自动事件不受采样影响。高频事件可以选用按事件名的采样率,覆盖全局采样率:
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ Framedash->SetEventSamplingRate(TEXT("ai_pathfind_step"), 0.05f); // 约 5% Framedash->RemoveEventSamplingRate(TEXT("ai_pathfind_step")); // 回到全局采样率}SetEventSamplingRate(const FString& EventName, float Rate) 设置采样率并钳制到 [0, 1],RemoveEventSamplingRate(const FString& EventName) 移除该覆盖。两者都可从 Blueprint 调用。
地图加载耗时采集
Section titled “地图加载耗时采集”自 UE5 SDK 0.1.6 起可用。子系统可以测量关卡加载所需的时间,并将其作为 map_load 事件上报,进入构建比对(perf-diff)回归门禁以及仪表盘的加载耗时图表。BeginMapLoad、EndMapLoad 和 ReportMapLoad 都可从 Blueprint 调用。
包裹一次你自行控制的加载:
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ Framedash->BeginMapLoad(TEXT("Level_01")); // ... 打开关卡 ... Framedash->EndMapLoad();}BeginMapLoad 会在不受暂停和时间膨胀(time dilation)影响的单调时钟上启动计时,EndMapLoad 则停止计时并发出事件。在 EndMapLoad 之前再次调用 BeginMapLoad,会替换掉当前挂起的测量。
如果自定义加载器或流式加载器已经自行测量了耗时,可以直接上报:
Framedash->ReportMapLoad(TEXT("Level_01"), 1234.0); // 加载耗时(毫秒)当加载耗时为 NaN、无穷大或负值时,ReportMapLoad 会整个丢弃该样本(不会钳制)。
两条路径都会发出携带 metrics["load_time_ms"] 与 attributes["map_name"] 的 map_load 事件。该事件有意将 map_id 留空,因此不会进入空间热力图和激活门禁。这些调用在游戏线程上执行,不会抛出异常,并且在初始化之前为空操作。当自定义加载器或流式加载器在工作线程上完成时,请在调用 EndMapLoad 或 ReportMapLoad 之前先切回游戏线程(例如借助 AsyncTask(ENamedThreads::GameThread, ...))。SDK 不会替你做线程编组,从其他线程发起的调用会静默丢弃该事件。
磁盘 I/O 指标
Section titled “磁盘 I/O 指标”自 UE5 SDK 0.1.6 起可用。SDK 可以把磁盘读取计数器以 io.read_bytes、io.read_time_ms、io.read_ops 这几个指标键附加到 perf_heartbeat 事件上。每个值都是相对上一次 heartbeat 的增量,并且只有在真正采集到样本之后这些键才会出现(不会填零)。与其他性能指标一样,io.* 会进入 perf-diff / builds-compare 回归门禁以及仪表盘图表。io.* 没有阈值告警。
自动采样是选择性开启的。在 Project Settings > Framedash 中启用 Track Disk IO(bTrackDiskIo,默认关闭),SDK 就会串接一个统计同步磁盘读取的 IPlatformFile 包装器。由 IoDispatcher / IoStore 路径(zen loader、Nanite 流式加载)处理的读取会绕过该包装器,因此在 Nanite 密集的 I/O 场景下计数会偏低。
ReportIoSample 可从 Blueprint 调用,无论该设置如何都会供给一个样本:
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ Framedash->ReportIoSample(/*Bytes=*/1048576, /*ReadTimeMs=*/3.2, /*Ops=*/12);}内存分类指标
Section titled “内存分类指标”UE5 SDK 0.1.7 及更高版本可用。启用 Track Memory Detail(bTrackMemoryDetail,默认关闭)后,SDK 会按 heartbeat 周期采样各内存分类的用量,并作为 mem.* 指标附加到 perf_heartbeat 事件上:
mem.vram:RHI 报告的使用中纹理显存(字节,即流式与非流式纹理分配之和)。从RHIGetTextureMemoryStats读取,无需特殊启动参数即可在任何 RHI 上使用。在无头 /-nullrhi构建中不会附加。mem.textures/mem.meshes/mem.audio:来自 Low-Level Memory 追踪器(LLM)各标签的字节数。仅当 LLM 编译进构建且在运行时启用(-llm)时才会附加。LLM 未启用时只会附加mem.vram。
未追踪的分类会让该键缺失(表示“未收集”,与已收集的 0 不同)。
除 perf_heartbeat 外,这些键还会附加到带位置(map_id 非空)的事件上。由于 perf_heartbeat 的 map_id 为空、不会进入空间热力图网格,SDK 会把同一采样也放到带位置的事件上,从而支持逐单元格的内存热力图。带位置的事件携带的是按 10 秒 heartbeat 周期刷新的缓存采样,不会发生逐事件采样。若你在 TrackWithData 的 metrics 映射中传入自己的 mem.* 键,则以你的值为准。
在 Project Settings > Plugins > Framedash > Track Memory Detail 启用,或在 Config/DefaultGame.ini 中设置 bTrackMemoryDetail=True。默认关闭,因此默认会话保持零分配的事件路径。mem.vram 同时也是 perf-diff(构建比对)的比较指标。
编辑器内云端热力图
Section titled “编辑器内云端热力图”UE5 SDK 0.1.7 及更高版本可用。插件的 FramedashEditor 模块新增了 Framedash Heatmap 标签页,可在编辑器内获取并显示云端聚合的热力图。从 Window 菜单 > Framedash > Framedash Heatmap 打开。
首先在 Project Settings > Plugins > Framedash Heatmap 设置读取 API 密钥(Read API Key,analytics:read 作用域)和项目 ID(Project ID)。API Base URL 默认为 https://app.framedash.dev。
在 UE5 SDK 0.1.11 及更高版本中,可以将 Read API Key 留空,并在启动 Unreal Editor 前设置 FRAMEDASH_ANALYTICS_API_KEY。环境变量的值不会保存到编辑器设置。如果在 Project Settings 中输入 Read API Key,该值优先于环境变量。
面板支持以下操作:
- 获取并选择地图列表
- 设置时间窗口(天数)、单元格大小和事件名筛选
- 获取云端聚合的热力图单元格
- 将关卡视口框定到已获取的完整热力图
在 UE5 SDK 0.1.13 及更高版本中,请在非 Play-in-Editor(PIE)状态下获取数据,然后在需要显示热力图的每个关卡视口中启用 Show > Framedash Heatmap。Show 标志默认关闭,并且每个视口相互独立。为避免干扰游戏测试,PIE 期间热力图会暂停显示,并在 PIE 结束后恢复各视口之前的选择。
具有实测 Z 坐标的单元格会在相应高度显示为云端体素;2D 响应仍保持平面显示。热力图在视口的主场景通道中渲染,因此会同时出现在标准 F9 和高分辨率视口截图中。
SDK 在 LogFramedash 类别下记录日志。集成期间要确认发送,请以详细日志运行游戏:
-LogCmds="LogFramedash Verbose"你也可以在控制台用 Log LogFramedash Verbose 在运行时启用。一次发送会记录 SendBatch: N events -> https://ingest.framedash.dev/v1/events,随后是 HTTP 结果。若事件未到达,见故障排查。
无头 / CI
Section titled “无头 / CI”要在没有交互式编辑器会话的情况下运行游戏(用于 CI 或验证),请先构建编辑器目标,再以 null RHI 启动游戏。
添加插件后,在命令行中构建编辑器目标,无需编辑器 UI:
"<EngineRoot>\Engine\Build\BatchFiles\Build.bat" <ProjectName>Editor Win64 Development -Project="<full path>\<ProjectName>.uproject"然后以 null RHI 启动游戏模式。请把 <MapPath> 替换为完整的地图路径。Third Person 模板在 UE 5.3-5.5 上自带 /Game/ThirdPerson/Maps/ThirdPersonMap,在 5.6 及更高版本上自带 /Game/ThirdPerson/Lvl_ThirdPerson:
UnrealEditor-Cmd.exe <Project>.uproject <MapPath> -game -nullrhi -nosound -unattended -nosplash -stdout-game 会启动一个无限的游戏循环,永远不会自行退出。在 CI 中请用外部超时或 kill 来限定运行时长,并把日志中的 HTTP 2xx 行(而非进程退出)作为成功信号。加上 -unattended 和 -nosplash 可让运行保持非交互。
由于游戏不会自行退出,请用一个在运行有足够时间刷新后 kill 进程的超时来包裹启动。最小的 PowerShell 包装示例:
$fdArgs = '"<Project>.uproject"','"<MapPath>"',"-game","-nullrhi","-nosound","-unattended","-nosplash","-stdout"$p = Start-Process -FilePath "UnrealEditor-Cmd.exe" -ArgumentList $fdArgs -PassThruif (-not $p.WaitForExit(120000)) { $p.Kill() } # 120 秒上限;进程不会自行终止路径参数用内嵌双引号('"<Project>.uproject"')包裹,因此包含空格的项目路径也不会在 Start-Process 参数拆分时损坏。在 Linux runner 上改用 timeout 120 UnrealEditor-Cmd ...。请为上限留出足够余量,让 HTTP 2xx 行在 kill 之前落地。GNU timeout 在终止运行时会以状态码 124 退出,因此请以日志中的 HTTP 2xx 行而非退出码作为成功判据,日志检查通过后将 124 视为预期结果。macOS 默认没有 GNU timeout:安装 coreutils(brew install coreutils)并使用 gtimeout,或用上面 PowerShell 那样的延迟后 kill 的方式包裹启动。
当你对 -stdout 做管道或重定向时,该流是块缓冲的,因此一次已经成功的运行可能看起来卡住了(例如停在 Waiting on static mesh...,但 HTTP 202 其实已经发生)。权威记录是 Saved/Logs/<Project>.log;在其中 grep SendBatch: 和 Batch sent successfully (HTTP。这些行只有在开启详细日志时才会出现,因此启动时请加上 -LogCmds="LogFramedash Verbose"(见上文详细日志)。如果必须实时解析 stdout,请加上 -FORCELOGFLUSH。
离线队列只在优雅关闭或临时发送失败时写入,硬性 kill 时不会写入。如果运行在传输完成刷新之前优雅关闭,缓冲的事件会写入 Saved/Framedash/offline-queue.json,并在下次初始化、世界开始 tick 后发送。相反,硬性 kill 会丢失仍在缓冲中的事件,因此在 CI 中不要依赖队列,请在 kill 进程之前等待日志中的 HTTP 2xx 行。请让运行保持足够长以完成刷新,或再运行一次以排空队列。见故障排查。
编辑器内快速开始示例
Section titled “编辑器内快速开始示例”插件在 Plugins/Framedash/Samples/InEditorQuickstart 附带了一个用于验证配置的示例。使用匹配的预编译包时,Blueprint 方案可在纯 Blueprint 项目中运行,无需编译 C++。已有 C++ 模块的项目也可以复制并编译随附的 C++ Actor(FramedashQuickstartActor)。
该示例假定两个前提:
- 具有
events:write范围的 Ingest API 密钥 - 通过仪表盘 Maps > Generate demo 注册的
map_id
两条路径在运行时都会发送一个绑定地图的 Track 事件,这正是在仪表盘中激活项目的触发条件。
Blueprint 方案
Section titled “Blueprint 方案”UFramedashSubsystem 在 Framedash 类别下可从 Blueprint 调用,因此你无需编写任何 C++,即可从 Blueprint 图表发送激活事件:
- 打开 Level Blueprint(Blueprints > Open Level Blueprint),从 Event BeginPlay 节点开始。
- 从 Get Game Instance 拖出,再添加一个 Get Subsystem 节点并将其类设置为 Framedash Subsystem。该输出引脚就是
UFramedashSubsystem实例。 - 从子系统引脚调用 Track(类别 Framedash)。将 Event Name 设为类似
quickstart_ping,将 Map Id 设为你生成的map_id,并将 Position 设为关卡内任意位置(例如 Player Start 的位置)。若还想附加attributes/metrics,请改用 Track With Data。 - 将 Event BeginPlay 连到 Track 调用,使其在运行时执行。
按下 Play,绑定地图的 Track 事件即会在仪表盘中激活项目。
CI / 自动会话
Section titled “CI / 自动会话”在自动化测试或性能剖析运行中,请为整个会话打标签,使每个事件都携带 CI 构建及其 branch/commit/scenario。在启动时调用一次自动会话 API(BuildId、Branch、Commit、Scenario 均为可选的 FString,且这些方法都可从 Blueprint 调用):
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ Framedash->BeginAutomatedSession( /*BuildId=*/TEXT("build-123"), /*Branch=*/TEXT("main"), /*Commit=*/TEXT("abc1234"), /*Scenario=*/TEXT("nightly")); // ... 运行自动化场景 ... Framedash->EndAutomatedSession();}在 CI 中,推荐使用 BeginAutomatedSessionFromEnvironment(),它会读取 FRAMEDASH_BUILD_ID、FRAMEDASH_GIT_BRANCH、FRAMEDASH_GIT_COMMIT 和 FRAMEDASH_TEST_SCENARIO(这些正是 framedash run-profile-test 导出的变量):
if (auto* Framedash = GetGameInstance()->GetSubsystem<UFramedashSubsystem>()){ Framedash->BeginAutomatedSessionFromEnvironment(); // ... 运行自动化场景 ... Framedash->EndAutomatedSession();}自动会话会为会话内的每个事件打上 build_id 覆盖和 ci.branch / ci.commit / ci.scenario 属性,这正是进入构建比对(perf-diff)CI 门禁的依据。它不会改变任何事件的 source:SDK 自身的自动事件(session_start、perf_heartbeat)始终是 source=automated,而你的 Track 事件始终是 source=player,在 CI 与正常游玩中都一样。完整流水线见 CI 剖析。