Unity SDK
說明如何使用 Framedash Unity SDK 自動收集效能遙測資料。
- Unity 2022.3 以上
- .NET Standard 2.1 / .NET Framework 4.x
Unity Package Manager(建議)
Section titled “Unity Package Manager(建議)”- 開啟 Window > Package Manager。Unity 6.5 將此路徑改名為 Window > Package Management > Package Manager;舊版本仍保留較短的 Window > Package Manager。
- 選擇「+」>「Add package from git URL…」
- 輸入以下 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,等同於之後呼叫 SetPlayerId。enableOfflineQueue 預設為 true;傳入 false 可停用磁碟離線佇列。完整簽章為 Initialize(string apiKey = null, string endpointUrl = null, string buildId = null, string playerId = null, bool enableOfflineQueue = true)。
效能資料自動收集
Section titled “效能資料自動收集”SDK 會自動收集以下資料:
- FPS: 影格率
- Frame Time: 每影格處理時間
- Memory: Unity Profiler 的總配置記憶體
傳送自訂事件
Section titled “傳送自訂事件”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 } });識別玩家(選用)
Section titled “識別玩家(選用)”預設情況下事件會以匿名方式傳送。玩家登入後呼叫 SetPlayerId,即可將後續事件與該玩家關聯:
TelemetrySDK.Instance.SetPlayerId(playerId);DAU 只計算非空的玩家 ID。匿名工作階段仍會計入事件數和工作階段數,但不會增加 DAU 或 MAU。請在希望納入玩家 KPI 的活動發生前呼叫 SetPlayerId。
執行時取樣覆寫
Section titled “執行時取樣覆寫”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_start、perf_heartbeat)不受取樣影響。
地圖載入耗時擷取
Section titled “地圖載入耗時擷取”自 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 之前為無操作。當自訂載入器或串流載入器在工作執行緒上完成時,請在呼叫 EndMapLoad 或 ReportMapLoad 之前先切回主執行緒(例如藉由播放器迴圈的更新,或擷取的 SynchronizationContext)。SDK 不會替你進行執行緒封送,從其他執行緒發起的呼叫會靜默丟棄該事件。
磁碟 I/O 指標
Section titled “磁碟 I/O 指標”自 Unity SDK 0.1.3 起可用。SDK 可以把磁碟讀取計數器以 io.read_bytes、io.read_time_ms、io.read_ops 這幾個指標鍵附加到 perf_heartbeat 事件上。每個值都是相對上一次 heartbeat 的增量,而且只有在真正擷取到樣本之後這些鍵才會出現(不會填零)。與其他效能指標一樣,io.* 會進入 perf-diff / builds-compare 迴歸閘門以及儀表板圖表。io.* 沒有閾值警示。
在 Unity Editor 和 Development Build 中,SDK 會自動取樣 AsyncReadManagerMetrics。發行版播放器不會擷取任何自動的 io.* 樣本。
記憶體分類指標
Section titled “記憶體分類指標”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_heartbeat 的 map_id 為空、不會進入空間熱力圖網格,SDK 會把同一取樣也放到帶位置的事件上,從而支援逐格的記憶體熱力圖。帶位置的事件攜帶的是按 heartbeat 週期更新的快取取樣,因此事件路徑不會讀取引擎。
呼叫方傳入的指標鍵一律優先,無論是鍵衝突還是容量:mem.* 只填入 50 個指標攝取上限以下剩餘的槽位,且優先填入 mem.vram。
編輯器內 SceneView 熱力圖
Section titled “編輯器內 SceneView 熱力圖”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 可能比你追蹤的數量多,這是預期行為,而非重複。若事件未到達,見疑難排解。
無頭 / CI
Section titled “無頭 / CI”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 啟動,並從結束的行程讀取結束碼:
$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 / 自動工作階段
Section titled “CI / 自動工作階段”在自動化測試或效能剖析執行中,請為整個工作階段打標籤,使每個事件都攜帶 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_ID、FRAMEDASH_GIT_BRANCH、FRAMEDASH_GIT_COMMIT 和 FRAMEDASH_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_start、perf_heartbeat)始終是 source=automated,而你的 Track 事件始終是 source=player,在 CI 與正常遊玩中都一樣。完整流水線見 CI 剖析。