リプレイ
はじめに
このドキュメントでは、決定論的なQuantumシミュレーションのリプレイを保存し再生する方法について説明します。
リプレイは、記録されたセッションから同じライブラリ、ゲームアセット、設定、ユーザー入力を使用してゲームシミュレーションを再実行するものです。これらは、キルカムのリプレイを実行するために少し過去の状態で第二のシミュレーションを開始する際にも使用できます。アプリケーションは同じものである必要はなく、同じプラットフォームで実行される必要もありませんが、同じゲームとQuantum DLLでビルドされている必要があります。オプションで、チェックサムを記録して再生時に検証することもできます。
Quantumリプレイを実行するには、次の4つの要素が必要です。
- 同じバージョンのゲームとQuantumライブラリでビルドされたアプリケーション
- QuantumDeterministic.dll および Quantum.Engine.dll
- Quantum.Simulation.dll
- ゲームアセット
- Quantumアセットデータベース(DB)
- ルックアップテーブルファイル(LUT)
- ゲームセッション特有の設定ファイル
- SimulationConfig
- RuntimeConfig
- ゲームセッション特有の入力履歴
リプレイを保存する方法
Unity
Quantumを開始する前に、RecordingFlags
をInput
またはAll
に設定します。
C#
[Flags]
public enum RecordingFlags {
None = 0, // 何も記録しない、デフォルト設定
Input = 1 << 0, // 入力を記録する
Checksums = 1 << 1, // チェックサムを記録する(有効にする必要がある)
All = Input | Checksums // 入力とチェックサムを記録する
}
QuantumRunnerLocalDebug
のインスペクターenum
フィールドを使用するか、

- Quantumメニューを使用する際の
QuantumMenuUIController.ConnectArgs
のインスペクターenum
フィールドを使用するか、

SessionRunner.Arguments
のフラグを設定するか、SessionRunner.Start()
を呼び出す際に,

- あるいは、
QuantumGame.StartRecordingInput()
を使用して入力記録を手動で開始することもできます。
PS:入力の記録は一般的に管理メモリの割り当てを生成します。
ゲームを開始し、次に以下のUnityエディタメニューオプションの1つを選択してリプレイを保存します。エクスポートされるファイルには、利便性のためにアセットDBを含むか、リプレイファイルのサイズを小さくするためにアセットDBを除外することができます。デフォルトのリプレイファイルの場所は Assets/QuantumUser/Replays
です。デフォルト名は現在読み込まれているマップ名と日時から構成されます。アセットDBを除外するリプレイの場合、-DB
の接尾辞の付いた2つ目のファイルがエクスポートされることがあります。
Quantum > Export > Replay (Include Asset DB)
Quantum > Export > Replay (Exclude Asset DB)
また、次のようにコード内でリプレイファイルを手動で作成して保存することもできます:
C#
var replay = quantumGame.GetRecordedReplay(includeChecksums: true, includeDb: false))
File.WriteAllText("replay.json", JsonUtility.ToJson(replay));
また、次のようにコード内でアセットDBをエクスポートすることもできます:
C#
using (var file = File.Create("db.json")) {
quantumGame.AssetSerializer.SerializeAssets(file, quantumGame.ResourceManager.LoadAllAssets().ToArray());
}
Webhook
Quantum Public Cloudで実行されているセッションからリプレイを保存するには、リプレイウェブフックを設定し、カスタムバックエンドを指す必要があります。
スタートのWebRequestは、SessionConfig
およびRuntimeConfig
設定ファイルと共に提供されます。チャンクは、IsLast
が最終的にtrue
になるまで頻繁に届き、すべてのプレイヤーからのストリーミング入力を表します。
リプレイをUnityで再生可能にするために、QuantumReplayFile
クラスにJSON形式でシリアル化するか、必要なデータを格納するための別のデータ構造を作成します。
リプレイを実行する方法
Unity
QuantumRunnerLocalDebug
スクリプトの代わりに、リプレイを使用してローカルゲームを開始するために QuantumRunnerLocalReplay
スクリプトを使用します。記録されたファイルをインスペクターの Replay File
フィールドにドラッグアンドドロップします。デフォルトのファイル命名規則が使用されている場合、アセット Database File
が自動的に検出されて設定されます。再生を押します。

このスクリプトは、リプレイファイルをロードし、引数を設定および構成し、Quantumセッションを開始するためのいくつかのステップを踏みます。このプロセスは、.Netアプリケーションランナーが行うことと似ており、カスタムリプレイ開始ロジックを作成するために使用できます。
ステップ1:リプレイファイルをデシリアライズします。
C#
var replayFile = JsonUtility.FromJson<QuantumReplayFile>(ReplayFile.text);
ステップ2:リプレイファイルから入力プロバイダーを作成します。入力がデルタ圧縮されているか、生の入力を使用しているかによって、自動的に正しいプロバイダーが作成されます。
C#
_replayInputProvider = replayFile.CreateInputProvider();
ステップ3:SessionRunner
引数を作成します。たとえば、バイナリRuntimeConfig
をデコードし、シミュレーションをDeterministicGameMode.Replay
モードに設定します。InitialTick
とFrameData
は、リプレイが最初から開始されない場合にのみ必要です。
C#
var serializer = new QuantumUnityJsonSerializer();
var runtimeConfig = serializer.ConfigFromByteArray<RuntimeConfig>(replayFile.RuntimeConfigData.Decode(), compressed: true);
var arguments = new SessionRunner.Arguments {
RunnerFactory = QuantumRunnerUnityFactory.DefaultFactory,
RuntimeConfig = runtimeConfig,
SessionConfig = replayFile.DeterministicConfig,
ReplayProvider = _replayInputProvider,
GameMode = DeterministicGameMode.Replay,
RunnerId = "LOCALREPLAY",
PlayerCount = replayFile.DeterministicConfig.PlayerCount,
InstantReplaySettings = InstantReplayConfig,
InitialTick = replayFile.InitialTick,
FrameData = replayFile.InitialFrameData,
DeltaTimeType = DeltaTypeType
};
ステップ4:リプレイとともに保存されたアセットデータベースを使用するか、外部のアセットデータベースソースを使用するか、異なる ResourceManager
を設定しないでください。
C#
var assets = replayFile.AssetDatabaseData?.Decode();
if (DatabaseFile != null) {
assets = DatabaseFile.bytes;
}
var serializer = new QuantumUnityJsonSerializer();
if (assets?.Length > 0) {
_resourceAllocator = new QuantumUnityNativeAllocator();
_resourceManager = new ResourceManagerStatic(serializer.AssetsFromByteArray(assets), new QuantumUnityNativeAllocator());
arguments.ResourceManager = _resourceManager;
}
ステップ5:最後にゲームを開始します。
C#
_runner = QuantumRunner.StartGame(arguments);
オプションで、チェックサムのリストを確認し、チェックサムの不一致をログに記録することも可能です。
C#
_runner.Game.StartVerifyingChecksums(replayFile.Checksums);
.Netアプリケーション
QuantumDotnetBuildSettings
アセットを選択し、Generate Dotnet Project
を押して、.Netシミュレーションプロジェクトを作成または更新します。
Assets/../Quantum.Dotnet/Quantum.Dotnet.sln
下のソリューションファイルを開いてコンパイルします。
コマンドラインからランナーを開始します。
Quantum.Dotnet\Quantum.Runner.Dotnet\bin\Debug> .\Quantum.Runner.exe --help
Description:
Main method to start a Quantum runner.
Usage:
Quantum.Runner [options]
Options:
--replay-path <replay-path> Path to the Quantum replay json file.
--lut-path <lut-path> Path to the LUT folder.
--db-path <db-path> Optionally an extra path to the Quantum database json file.
--checksum-path <checksum-path> Optionally an extra path to the checksum file.
--version Show version information
-?, -h, --help Show help and usage information
例:
Quantum.Dotnet\Quantum.Runner.Dotnet\bin\Debug>.\Quantum.Runner.exe
--replay-path ..\..\..\..\Assets\QuantumUser\Replays\MapTestNavMeshAgents-2024-06-17-14-19-46.json
--lut-path ..\..\..\..\Assets\Photon\Quantum\Resources\LUT
注意:Unityで記録されたチェックサムは、Dotnetランナーで生成されたものとは互換性がありません。SessionConfig
でChecksumCrossPlatformDeterminism
を有効にして、異なるプラットフォームで記録されたチェックサムを検証します。
PlayerIsLocal()
Quantum.Game.Session
のPlayerIsLocalチェックは、カメラやオーディオ、VFXのフォーカスなど、多くのビュー制御に役立ちます。しかし、リプレイでは機能しません。
例えば、ウェブフックを使用してオンラインでキャプチャされたリプレイには、この情報は決して含まれません。
リプレイをサポートするためには、PlayerIsLocalチェックを最初にセッションゲームモードのチェックにラッピングする方が良いです。
C#
public class CustomViewContext : MonoBehaviour, IQuantumViewContext
{
private PlayerRef _focusedPlayer;
public bool IsFocusedPlayer(QuantumGame game, PlayerRef player)
{
if (game.Session.GameMode == DeterministicGameMode.Replay)
{
return player == _focusedPlayer;
}
return game.PlayerIsLocal(player);
}
}
QuantumReplayFile API
QuantumReplayFile
は、Quantumリプレイを実行するために関連するすべてのデータを保持し、JSON形式でシリアル化できます。
リプレイには、InputHistoryDeltaCompressed
またはInputHistoryLegacy
として保存された有効な入力履歴を含める必要があります。前者は、ファイルサイズをより効率的に抑え、Photon Public Cloudからの入力ストリーミングが行われる唯一のモードです。
RuntimeConfigData
はバイナリシリアライズ形式(Quantum.AssetSerializer)で保存されており、リプレイストリーミング中に受信されるデータと同様です。
リプレイには、利便性のためにシリアル化された(AssetSerializer)アセットデータベースAssetDatabaseData
を含めることができますが、ファイルサイズが問題となるプロダクション環境では省略すべきです。
リプレイには、開発機能としてランタイム中に検証可能なチェックサムが含まれている場合があります。
QuantumJsonFriendlyDataBlob
は、UnityのJSONツールがバイト配列のみを詳細にシリアル化する問題を回避するために、JSON内でバイナリデータを格納するためのラッパーです。
C#
public class QuantumReplayFile {
// デルタ圧縮されたバイナリ入力履歴、これは例としてリプレイウェブフックで送信されるものと同じです。
public QuantumJsonFriendlyDataBlob InputHistoryDeltaCompressed;
// Quantum 2.1で使用された完全な詳細入力であり、機能はしますが、限られたユースケースにのみ対応しています。
public DeterministicTickInputSet[] InputHistoryLegacy;
// バイナリシリアライズ化されたRuntimeConfig。
// Use AssetSerializer.ConfigToByteArray(runtimeConfig, compress: true)
/// </summary>
public QuantumJsonFriendlyDataBlob RuntimeConfigData;
/// セッション設定。
public DeterministicSessionConfig DeterministicConfig;
/// 入力の最終ティック。
public int LastTick;
/// 開始するための初期ティック。 <see cref="InitialFrameData"/> の設定が必要です。
public int InitialTick;
/// リプレイを開始するためのオプションのフレームデータ。このデータはセーブゲームなどに使用されます。
public byte[] InitialFrameData;
/// オプションのチェックサム。これをプロダクション環境でのリプレイから省略してください。
public ChecksumFile Checksums;
/// オプションのシリアル化されたアセットデータベース。これをプロダクション環境でのリプレイから省略してください。
/// Use AssetSerializer.SerializeAssets(stream, ResourceManager.LoadAllAssets().ToArray()
public QuantumJsonFriendlyDataBlob AssetDatabaseData;
}
C#
public class QuantumJsonFriendlyDataBlob {
/// バイト配列はそのまま保存されます。
public byte[] Binary;
/// バイト配列はBase64テキストとして保存されます。
public string Base64;
/// BinaryとBase64の両方はGZip圧縮が可能です。
public bool IsCompressed;
}
インスタントリプレイ
インスタントリプレイ、例えばゲーム中に発生するキルカムリプレイを行う方法を示すスクリプトを提供しています。このスクリプトは補助的なQuantumRunnerで行われ、インスタントリプレイが終了するとデフォルトランナーに戻ります。
使用するには、UnityコンポーネントであるQuantumInstantReplayDemo
をGame Objectに追加し、セットアップ(再生速度、リプレイ時間などを設定)を行い、ゲームプレイ中にスタートとストップボタンを押します。
