跳到內容

Unity SDK

說明如何使用 Framedash Unity SDK 自動收集效能遙測資料。

  • Unity 2022.3 以上
  • .NET Standard 2.1 / .NET Framework 4.x
  1. 開啟 Window > Package Manager。Unity 6.5 將此路徑改名為 Window > Package Management > Package Manager;舊版本仍保留較短的 Window > Package Manager
  2. 選擇「+」>「Add package from git URL…」
  3. 輸入以下 URL:
https://github.com/crane-valley/framedash-unity-sdk.git

如需鎖定至特定版本,請在 URL 後加上版本標籤:

https://github.com/crane-valley/framedash-unity-sdk.git#v0.1.7

若要從指令碼或 CI 安裝,請直接在 Packages/manifest.json 中加入相依。套件名稱是 com.framedash.sdk

{
"dependencies": {
"com.framedash.sdk": "https://github.com/crane-valley/framedash-unity-sdk.git#v0.1.7"
}
}

在啟動時初始化一次 SDK,例如在持續存在的 GameObject 上的 MonoBehaviour 中初始化:

using System.Collections.Generic;
using Framedash;
using UnityEngine;
public sealed class GameBootstrap : MonoBehaviour
{
// 在 Inspector 中設定 API 金鑰,或留空以回退到 FRAMEDASH_API_KEY
// 環境變數。完整的解析順序與平台注意事項見下方說明。
[SerializeField] private string _apiKey;
private void Awake()
{
TelemetrySDK.Initialize(
apiKey: _apiKey,
buildId: Application.version);
}
}

endpointUrl 為選用,省略時預設使用 https://ingest.framedash.dev/v1/events;如需指向本機或自架的 ingest,請明確傳入。playerId 會在初始化時設定玩家 ID,等同於之後呼叫 SetPlayerIdenableOfflineQueue 預設為 true;傳入 false 可停用磁碟離線佇列。完整簽章為 Initialize(string apiKey = null, string endpointUrl = null, string buildId = null, string playerId = null, bool enableOfflineQueue = true)

SDK 會自動收集以下資料:

  • FPS: 影格率
  • Frame Time: 每影格處理時間
  • Memory: Unity Profiler 的總配置記憶體
TelemetrySDK.Instance.Track(
eventName: "player_death",
mapId: "map_01",
position: transform.position);

如需附加分類屬性或數值指標,可傳入可選的字典:

// 請確保在檔案頂部添加了 'using System.Collections.Generic;'
TelemetrySDK.Instance.Track(
eventName: "player_death",
mapId: "map_01",
position: transform.position,
attributes: new Dictionary<string, string> { { "cause", "fall_damage" } },
metrics: new Dictionary<string, float> { { "health", 0f } });

預設情況下事件會以匿名方式傳送。玩家登入後呼叫 SetPlayerId,即可將後續事件與該玩家關聯:

TelemetrySDK.Instance.SetPlayerId(playerId);

DAU 只計算非空的玩家 ID。匿名工作階段仍會計入事件數和工作階段數,但不會增加 DAU 或 MAU。請在希望納入玩家 KPI 的活動發生前呼叫 SetPlayerId

SDK 會套用一個全域取樣率(預設 1.0 = 全部保留)。高頻事件可以在執行時選用依事件名稱的取樣率,覆寫全域取樣率:

TelemetrySDK.Instance.SetEventSamplingRate("ai_pathfind_step", 0.05f); // 約 5%
TelemetrySDK.Instance.RemoveEventSamplingRate("ai_pathfind_step"); // 回到全域取樣率

SetEventSamplingRate(string eventName, float rate) 設定該事件的取樣率,rate 會被夾在 [0, 1]。RemoveEventSamplingRate(string eventName) 移除該覆寫,使事件回到全域取樣率。自動收集的事件(session_startperf_heartbeat)不受取樣影響。

自 Unity SDK 0.1.3 起可用。SDK 可以測量地圖或關卡載入所需的時間,並將其作為 map_load 事件回報。該載入耗時會進入建置比對(perf-diff)迴歸閘門,以及儀表板的載入耗時圖表。

BeginMapLoad / EndMapLoad 包住一次你自行控制的載入:

TelemetrySDK.Instance.BeginMapLoad("Level_01");
// ... 載入場景 ...
TelemetrySDK.Instance.EndMapLoad();

BeginMapLoad 會在不受暫停與時間縮放影響的單調時鐘上啟動計時,EndMapLoad 則停止計時並發出事件。在 EndMapLoad 之前再次呼叫 BeginMapLoad,會替換掉目前擱置中的測量。

如果自訂載入器或串流載入器已經自行測量了耗時,可以直接回報:

TelemetrySDK.Instance.ReportMapLoad("Level_01", 1234f); // 載入耗時(毫秒)

loadTimeMs 為 NaN、無限大或負值時,ReportMapLoad 會整個丟棄該樣本(不會夾限)。

兩條路徑都會發出攜帶 metrics["load_time_ms"]attributes["map_name"]map_load 事件。該事件刻意將 map_id 留空,因此不會進入空間熱力圖和啟用閘門。這些呼叫在主執行緒上執行,不會擲出例外,且在 Initialize 之前為無操作。當自訂載入器或串流載入器在工作執行緒上完成時,請在呼叫 EndMapLoadReportMapLoad 之前先切回主執行緒(例如藉由播放器迴圈的更新,或擷取的 SynchronizationContext)。SDK 不會替你進行執行緒封送,從其他執行緒發起的呼叫會靜默丟棄該事件。

自 Unity SDK 0.1.3 起可用。SDK 可以把磁碟讀取計數器以 io.read_bytesio.read_time_msio.read_ops 這幾個指標鍵附加到 perf_heartbeat 事件上。每個值都是相對上一次 heartbeat 的增量,而且只有在真正擷取到樣本之後這些鍵才會出現(不會填零)。與其他效能指標一樣,io.* 會進入 perf-diff / builds-compare 迴歸閘門以及儀表板圖表。io.* 沒有閾值警示。

在 Unity Editor 和 Development Build 中,SDK 會自動取樣 AsyncReadManagerMetrics。發行版播放器不會擷取任何自動的 io.* 樣本。

Unity SDK 0.1.4 及更新版本可用。SDK 會按 heartbeat 週期取樣各記憶體分類的用量,並作為 mem.* 指標自動附加到 perf_heartbeat 事件上。與 UE5 的 bTrackMemoryDetail 不同,無需任何 opt-in。

  • mem.vram:圖形驅動已配置的顯存(位元組),從 Profiler.GetAllocatedMemoryForGraphicsDriver 讀取。
  • mem.heap:受控堆積用量(位元組),從 Profiler.GetMonoUsedSizeLong 讀取。

讀數為 0 的取樣會被略過:鍵缺失表示該值未收集,SDK 絕不會傳送捏造的 0

perf_heartbeat 外,這些鍵還會附加到帶位置(map_id 非空)的事件上。由於 perf_heartbeatmap_id 為空、不會進入空間熱力圖網格,SDK 會把同一取樣也放到帶位置的事件上,從而支援逐格的記憶體熱力圖。帶位置的事件攜帶的是按 heartbeat 週期更新的快取取樣,因此事件路徑不會讀取引擎。

呼叫方傳入的指標鍵一律優先,無論是鍵衝突還是容量:mem.* 只填入 50 個指標攝取上限以下剩餘的槽位,且優先填入 mem.vram

Unity SDK 0.1.4 及更新版本可用。僅供編輯器使用的 Framedash.Editor 組件會從 Framedash REST API 取得專案的地圖與雲端彙整的熱力圖格子,並在 Unity 編輯器內繪製。

它需要具備 analytics:read 範圍的讀取 API 金鑰(Read API Key)以及專案 ID(Project ID),絕不是遊戲使用的唯寫 Ingest 金鑰。

在 Unity SDK 0.1.6 及更新版本中,可以將 Read API Key 留空,並在啟動 Unity 前設定 FRAMEDASH_ANALYTICS_API_KEY。環境變數的值不會儲存到 UserSettings/。若明確輸入 Read API Key,該值優先於環境變數。

取得到的熱力圖格子會按記錄的世界座標,在 SceneView 中繪製為半透明四邊形。無需封裝建置或開啟儀表板,即可在編輯器內查看空間熱力圖。

設定按專案保存在 UserSettings/ 下,該目錄不會封裝,也不會被版本控制追蹤。

首次整合時,請啟用詳細記錄以確認傳送:

TelemetrySDK.Instance.VerboseLogging = true;

清除成功會記錄 [Framedash] Flushed N events (HTTP 202)。在你呼叫 SetPlayerId 之前,每個工作階段還會記錄警告 [Framedash] No player_id set. Events will be sent as anonymous...;這是資訊提示,而非錯誤。自動收集的事件(初始化時的 session_start 和每 10 秒的 perf_heartbeat)會與你的手動事件合併到同一批次,因此 Flushed N events 可能比你追蹤的數量多,這是預期行為,而非重複。若事件未到達,見疑難排解

SDK 在 Unity 播放迴圈上傳送:Flush 透過協程和 UnityWebRequest 傳送,兩者只在迴圈執行時推進。裸的 Unity.exe -batchmode -executeMethod ... 呼叫執行在 Edit 模式,協程不 tick,因此事件被緩衝卻從不傳送。

要從 CI 傳送遙測,請用 PlayMode 測試(Unity Test Framework)驅動播放迴圈。進入 Play 模式,初始化 SDK,追蹤你的事件,並讓測試執行足夠長時間,以便在結束前完成一次清除:

using System.Collections;
using Framedash;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public sealed class TelemetrySmokeTest
{
[UnityTest]
public IEnumerator SendsAMarkerEvent()
{
TelemetrySDK.Instance.VerboseLogging = true;
// 從 FRAMEDASH_API_KEY 環境變數讀取金鑰,不在原始碼中硬編碼金鑰。
TelemetrySDK.Initialize(buildId: "ci-smoke");
// 如果專案的全域 SamplingRate 小於 1.0,標記事件可能被取樣丟棄,而自動傳送的
// session_start 仍回傳 HTTP 2xx,於是日誌斷言在沒有標記的情況下也通過(驗證誤報)。
// 將該事件的取樣率固定為 1.0,確保標記始終被保留。
TelemetrySDK.Instance.SetEventSamplingRate("ci_marker", 1f);
TelemetrySDK.Instance.Track(eventName: "ci_marker", mapId: "ci", position: default);
// 等待超過 10 秒的自動 heartbeat 間隔,讓至少一個 perf_heartbeat 觸發,然後把
// Flush() 作為最後一個 SDK 呼叫,其後不再追蹤任何事件。預設清除間隔(30秒)比這個
// 測試長,因此明確強制傳送,並在結束前等待 HTTP 請求完成。
yield return new WaitForSeconds(12f);
TelemetrySDK.Instance.Flush(); // 最後一次清除;單一事件不會觸發批次閾值
yield return new WaitForSeconds(3f); // 結束前的最小等待;慢速網路下傳送可能仍在進行,因此請透過下面的 HTTP 202 記錄行確認送達
}
}

此測試需要自己的組件定義,Unity Test Framework 才能編譯並探索它。請將下面的 .asmdef 放在測試檔案旁邊:

{
"name": "Framedash.SmokeTests",
"references": [
"UnityEngine.TestRunner",
"Framedash.Runtime"
],
"includePlatforms": [],
"excludePlatforms": [],
"defineConstraints": ["UNITY_INCLUDE_TESTS"],
"precompiledReferences": ["nunit.framework.dll"],
"autoReferenced": false,
"overrideReferences": true
}

建議的配置是把 .asmdef 和測試檔案一起放在 Assets/Tests/PlayMode/

Assets/
Tests/
PlayMode/
Framedash.SmokeTests.asmdef
TelemetrySmokeTest.cs

在 CI 中以下列命令執行測試:

Unity.exe -batchmode -nographics -projectPath <path> -runTests -testPlatform PlayMode -testResults <path>\results.xml -logFile -

在 Windows 上,請讓 shell 等待真正結束。Unity.exe -batchmode ... -runTests 會在約 2-3 秒內返回 shell,而編輯器仍在執行,測試大約 20 秒後才結束,因此讀取即時結束碼的指令碼會看到虛假的通過。用 Start-Process -Wait -PassThru 啟動,並從結束的行程讀取結束碼:

Terminal window
$proc = Start-Process -FilePath "Unity.exe" -Wait -PassThru -ArgumentList @(
"-batchmode", "-nographics",
"-projectPath", '"<path>"',
"-runTests", "-testPlatform", "PlayMode",
"-testResults", '"<path>\results.xml"',
"-logFile", '"<path>\unity.log"'
)
if ($proc.ExitCode -ne 0) { throw "Unity tests failed (exit code $($proc.ExitCode))" }
$resultsPath = "<path>\results.xml"
if (-not (Test-Path -LiteralPath $resultsPath)) { throw "Unity did not write test results" }
[xml]$results = Get-Content -Raw -LiteralPath $resultsPath
$testRun = $results.'test-run'
if (-not $testRun) { throw "Invalid test results XML format" }
$testCount = if ($testRun.testcasecount) { [int]$testRun.testcasecount } else { [int]$testRun.total }
if ($testCount -lt 1) { throw "Unity discovered zero PlayMode tests" }

-Wait 會封鎖到 Unity 真正結束,-PassThru 回傳行程,因此 $proc.ExitCode 反映測試結果(0 = 通過)。每個路徑佔位符都用內嵌雙引號包住('"<path>"'),使包含空格的工作區路徑(例如 C:\build agent\game)在 Start-Process 拆分引數時不會被截斷。請把 -logFile 指向真實檔案而非 -,以便事後檢查,並解析 <path>\results.xml 取得每個測試的結果。僅檢查結束代碼並不足夠。Unity 即使發現 0 個測試也可能成功結束,因此必須在 XML 中確認至少有 1 個結果。

測試通過本身並不能證明遙測已送達:flush 仍在途中或已失敗時,測試也可能通過。在把流水線判為通過之前,請確認送達。在 Unity 記錄中 grep 攜帶 HTTP 202 的 flush 成功行:

[Framedash] Flushed N events (HTTP 202)

或者用 REST API 查詢你傳送的標記事件。只有當其中之一確認事件已落庫後,才把流水線判為通過。

CI 中由 FRAMEDASH_API_KEY 提供金鑰(上面的範例依賴於此,因此不會寫死任何金鑰)。呼叫 TelemetrySDK.Initialize(buildId: ...)(如上)或無引數的 TelemetrySDK.Initialize()

啟用離線佇列時(預設),事件會在正常退出或暫時性傳送失敗時保存到離線佇列,並在下次初始化時傳送。強制 kill 會遺失仍在緩衝中的事件,因此在 CI 中不要依賴佇列,請在 kill 行程之前等待記錄中的 HTTP 202 行。當 enableOfflineQueue: false 時,未清除的事件會被直接丟棄。見疑難排解

在自動化測試或效能剖析執行中,請為整個工作階段打標籤,使每個事件都攜帶 CI 建置及其 branch/commit/scenario。在測試進入點呼叫一次自動工作階段 API:

using Framedash;
// 所有引數皆為選填。
TelemetrySDK.Instance.BeginAutomatedSession(
buildId: "build-123",
branch: "main",
commit: "abc1234",
scenario: "nightly");
// ... 執行自動化情境 ...
TelemetrySDK.Instance.EndAutomatedSession();

完整簽章為 void BeginAutomatedSession(string buildId = null, string branch = null, string commit = null, string scenario = null)。在 CI 中,建議使用 BeginAutomatedSessionFromEnvironment(),它會讀取 FRAMEDASH_BUILD_IDFRAMEDASH_GIT_BRANCHFRAMEDASH_GIT_COMMITFRAMEDASH_TEST_SCENARIO(這些正是 framedash run-profile-test 匯出的變數):

TelemetrySDK.Instance.BeginAutomatedSessionFromEnvironment();
// ... 執行自動化情境 ...
TelemetrySDK.Instance.EndAutomatedSession();

自動工作階段會為工作階段內的每個事件標上 build_id 覆寫和 ci.branch / ci.commit / ci.scenario 屬性,這正是進入建置比對(perf-diff)CI 閘門的依據。它不會改變任何事件的 source:SDK 自身的自動事件(session_startperf_heartbeat)始終是 source=automated,而你的 Track 事件始終是 source=player,在 CI 與正常遊玩中都一樣。完整流水線見 CI 剖析