コンテンツにスキップ

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

特定のリリースに固定するには、バージョンタグを付与します:

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
{
// API キーは Inspector で設定するか、空のままにして 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 の総割り当てメモリ
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 を呼び出してください。

ランタイムのサンプリングオーバーライド

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_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); // ロード時間(ミリ秒)

ReportMapLoad は、loadTimeMs が NaN、無限大、または負の値のとき、そのサンプルを丸ごと破棄します(クランプはしません)。

どちらの経路も、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 の周期でメモリカテゴリ別の使用量をサンプリングし、perf_heartbeat イベントに mem.* メトリクスとして自動的に付与します。 UE5 の bTrackMemoryDetail と違い、オプトインは不要です。

  • mem.vram:グラフィックスドライバーが確保したメモリ(バイト)。Profiler.GetAllocatedMemoryForGraphicsDriver から読み取ります。
  • mem.heap:マネージドヒープの使用量(バイト)。Profiler.GetMonoUsedSizeLong から読み取ります。

読み取り値が 0 のときは、そのキーを付けません。 キーがないことは未収集を意味し、0 として送ることはありません。

これらのキーは perf_heartbeat に加えて、位置情報付き(map_id が空でない)イベントにも付与されます。 perf_heartbeatmap_id が空で空間ヒートマップのグリッドに入らないため、セルごとのメモリヒートマップを作れるよう、同じサンプルを位置情報付きイベントにも載せます。 位置情報付きイベントに載るのは 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 がトラックした件数より多くなることがありますが、これは重複ではなく想定どおりの挙動です。 イベントが届かない場合はトラブルシューティングを参照してください。

SDK は Unity のプレイヤーループ上で送信します。 Flush はコルーチンと UnityWebRequest を通して送信され、これらはループが回っている間しか進みません。 素の Unity.exe -batchmode -executeMethod ... は Edit モードで実行され、コルーチンが進まないため、イベントはバッファされますが送信されません。

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 を少なくとも 1 回
// 発火させてから、Flush() を最後の SDK 呼び出しにする。Flush() の後で何も
// トラックしないこと。既定のフラッシュ間隔(30秒)はこのテストより長いため、
// 明示的に送信を強制し、終了前に HTTP 送信の完了を待つ。
yield return new WaitForSeconds(12f);
TelemetrySDK.Instance.Flush(); // 最後のフラッシュ。イベント1件ではバッチ閾値に達しない
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 では、シェルが実際の終了を待つように起動してください。 Unity.exe -batchmode ... -runTests は約 2〜3 秒でシェルに戻りますが、エディターはそのまま動き続け、テストが終わるのはおよそ 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 = 成功)を反映します。XML の検査も同じく重要です。Unity はテストを 0 件しか検出しなくても正常終了する場合があるため、CI では少なくとも 1 件の結果を必須にしてください。 各パスは埋め込みの二重引用符で囲んでいるため('"<path>"')、空白を含むワークスペースのパス(たとえば C:\build agent\game)でも Start-Process の引数分割で壊れません。 -logFile- ではなく実ファイルに向けて後から確認できるようにしてください。

テストが成功しただけでは、テレメトリが届いた証明にはなりません。 フラッシュがまだ送信中でも、あるいは失敗していても、テストは成功しうるからです。 パイプラインを成功とみなす前に、送信を確認してください。 Unity のログから、HTTP 202 を含むフラッシュ成功行を grep します。

[Framedash] Flushed N events (HTTP 202)

または、送信したマーカーイベントを REST API で問い合わせます。 いずれかでイベントの到達を確認できたときだけ、パイプラインを成功とみなしてください。

CI では FRAMEDASH_API_KEY がキーを供給します。上のサンプルはこれに依存しており、キーをハードコードしません。TelemetrySDK.Initialize(buildId: ...)(上記)または引数なしの TelemetrySDK.Initialize() を呼び出します。

オフラインキューが有効な場合(既定)、イベントは正常終了時または一時的な送信失敗時にキューへ保存され、次の初期化時に送信されます。 強制終了ではバッファに残ったイベントが失われるため、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 run-profile-test が書き出す環境変数 FRAMEDASH_BUILD_IDFRAMEDASH_GIT_BRANCHFRAMEDASH_GIT_COMMITFRAMEDASH_TEST_SCENARIO を読み取ります。

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 プロファイリング を参照してください。