やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Anjinでシナリオ終了前N秒の動画を保存するレポーターの実装サンプル(Instant Replay for Unityパッケージを利用)

株式会社ディー・エヌ・エー(以下DeNA)が公開しているオープンソースのUnity向けオートパイロットフレームワーク Anjin(あんじん)は、シナリオ終了時に動作するReporterを設定できます。 ビルトインではJUnit形式のテストレポートを出力する JUnitXmlReporterスクリーンショット付きでSlack投稿を行なう SlackReporter が提供されていますが、ゲームタイトル個々にニーズに合うものを実装して使用できます。

本記事では、株式会社サイバーエージェントが公開しているオープンソースのプレイ動画保存ライブラリ Instant Replay for Unity パッケージを使用して、Anjinのシナリオ終了前N秒の動画を保存するレポーターを実装する方法を紹介します。

blog.sge-coretech.com

環境

  • Unity 6000.0.23f1
  • Anjin v1.9.0
  • Instant Replay for Unity v0.2.0

動作するプロジェクトは、Unity社の提供する2Dシューターサンプル GalacticKittens をフォークしたリポジトリinstant-replay ブランチにあります。

Instant Replay for Unity のインストール

まず、UnityNuGetから次のNuGetパッケージをインストールします。

  • System.IO.Pipelines
  • System.Threading.Channels

2025年3月以降、UnityNuGetレジストリはOpenUPMにホスティングされ、package.openupm.com レジストリからアップリンクされているため、透過的に使用できます。 詳しくは次の記事を参照してください。

www.nowsprinting.com

続いて Instant Replay パッケージをインストールします。 こちらはGitHubリポジトリのURLを指定します。

https://github.com/CyberAgentGameEntertainment/InstantReplay.git?path=Packages/jp.co.cyberagent.instant-replay

VideoRecordingReporter の実装

AnjinのReporterは、AbstractReporter を継承して実装します。 2つのメソッドを実装します。

Instant Replay セッションの開始

AnjinのReporterは、Anjinの実行終了時にのみ明示的に呼び出されます。専用の初期化メソッドはありません。

そこで、Instant Replayセッションを開始する契機には InitializeOnLaunchAutopilot 属性を使用します。この属性を付与したstaticメソッドは、Anjinのシナリオ開始時にコールバックを受けられます。 ここでは、実際に実行されるAutopilot設定に紐づいた VideoRecordingReporter を探してInstant Replayセッションを開始します。

なお、numFramesなどの引数はReporterのシリアライズフィールドに指定された値を渡しています。

[InitializeOnLaunchAutopilot]
private static void InitializeReporter()
{
    foreach (var reporter in AutopilotState.Instance.settings.reporters.OfType<VideoRecordingReporter>())
    {
        reporter._session = new InstantReplaySession(
            numFrames: reporter.NumFrames,
            fixedFrameRate: reporter.FixedFrameRate,
            maxWidth: reporter.MaxWidth,
            maxHeight: reporter.MaxHeight);
    }
}

動画の書き出し

Anjinのシナリオ実行が終了するとき、Reporterの PostReportAsync メソッドが呼ばれます。 ここでInstant Replayセッションを停止し、動画をファイルに書き出します。

public override async UniTask PostReportAsync(string message, string stackTrace, ExitCode exitCode, CancellationToken cancellationToken = new CancellationToken())
{
    var outputPath = await _session.StopAndTranscodeAsync(ct: cancellationToken);
    if (outputPath != null)
    {
        var exportPath = Path.Combine(AutopilotState.Instance.settings.ScreenshotsPath, $"{this.name}.mp4");
        File.Move(outputPath, exportPath);
    }
    else
    {
        Debug.LogWarning("Video Exporting failed.");
    }

    _session.Dispose();
}

Reporterアセットの設定

実装した VideoRecordingReporter は ScriptableObject です。コンテキストメニューから.assetファイルを生成し、動画に関する設定を行ないます。

実装例では最大フレーム数、フレームレート、画面サイズを指定できるようにしてあります。 シナリオの実行に失敗したときの調査用途を想定しているので、解像度は低めにしてみました。フレームレートももっと下げていいはず。

設定したアセットをオートパイロット設定ファイルの Reporters に追加すれば動作します。

補足

VideoRecordingReporterの運用についての補足

本記事では動画を保存する単独のReporterを実装する方法を紹介しました。 動画は、実行したマシンのローカルディスクに保存されます。 これは、GitHub ActionsやJenkinsによって定期実行され、問題が生じたときにアーティファクトを参照する運用を想定しています。

もしシナリオ実行に問題があったときのSlack投稿に動画を添付したいニーズがあるなら、ビルトインの SlackReporter をコピーして独自Reporterを作り、そこに本記事の内容をマージすることで実現できます。

AgentにInstant Replayを組み込む場合の補足

Instant ReplayをReporterでなくAgentに組み込むアプローチも考えられます。 用途によっては機能しますが、制限が生じるためお勧めしません。 Anjinは停止時、Agentの実行タスクをキャンセルし、その終了を待たずにプロセスを終了します。 そのためテストシナリオの終了契機で動画を書き出しをはじめても、完了できずに終了してしまうでしょう。

ただ、Agentとして利用したいニーズはありそうなので、Anjin本体の変更も視野に入れて検討はしてみます。 それも踏まえて、本記事の VideoRecordingReporter は当面ビルトインしません。

参考

Anjinは、本記事で紹介したReporterのほかにも Agent、Loggerをさまざまに拡張できます。 詳しくは『Anjin非公式ファンブック』を参照してください。

ikagoya.booth.pm

『Anjin非公式ファンブック』は、5/31から開催される技術書典18でも頒布予定です。

techbookfest.org