Replays
簡介
本文檔說明如何儲存和回放決定性 Quantum 模擬重播。
重播是使用相同的函式庫、遊戲資產、配置和使用者輸入從已記錄的遊戲會話中重新運行遊戲模擬。它們也可以用來啟動一個稍微處於過去狀態的第二個模擬,例如,用於執行擊殺鏡頭重播。應用程式不必是同一個,甚至不必運行在同一平台上,但必須使用相同的遊戲和 Quantum dll 建置。可選地,可以在播放期間記錄並驗證校驗和。
運行 Quantum 重播需要四個部分:
- 使用相同版本的遊戲和 Quantum 函式庫建置的應用程式
- QuantumDeterministic.dll and Quantum.Engine.dll
- Quantum.Simulation.dll
- 遊戲資產
- Quantum資產資料庫 (DB)
- 查閱表檔案 (LUT)
- 遊戲會話特定的配置檔案
- SimulationConfig
- RuntimeConfig
- 遊戲會話特定的輸入歷史記錄
如何儲存重播
Unity
在啟動 Quantum 之前,將RecordingFlags設定為Input或All。
C#
[Flags]
public enum RecordingFlags {
None = 0, // records nothing, default setting
Input = 1 << 0, // records input
Checksums = 1 << 1, // records checksums (must be enabled)
All = Input | Checksums // recorded input and checksum
}
- 使用
QuantumRunnerLocalDebug的檢查器enum欄位,或
- 使用 Quantum 選單時,使用
QuantumMenuUIController.ConnectArgs中的檢查器enum欄位,或
- 在呼叫
SessionRunner.Start()時,在SessionRunner.Arguments上設定標誌,或
- 或者,也可以使用
QuantumGame.StartRecordingInput()手動開始輸入記錄。
PS:輸入記錄通常會產生託管記憶體配置。
啟動遊戲,然後透過選擇下面的一個 Unity 編輯器選單選項來匯出重播檔案。匯出的檔案可以為了方便而包含資產資料庫,或者排除資產資料庫以保持重播檔案體積小巧。預設的重播檔案位置是Assets/QuantumUser/Replays。預設名稱由當前載入的地圖名稱和日期時間組成。對於排除的資產資料庫,它可能會匯出一個帶有-DB後綴的第二個檔案。
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));
或者在程式碼中匯出資產資料庫:
C#
using (var file = File.Create("db.json")) {
quantumGame.AssetSerializer.SerializeAssets(file, quantumGame.ResourceManager.LoadAllAssets().ToArray());
}
Webhook
要儲存在 Quantum 公有雲上運行的會話的重播,必須設定重播 Webhook 並指向自訂後端。
開始的 WebRequest 帶有SessionConfig和RuntimeConfig配置檔案。區塊將會頻繁到達,直到IsLast最終為true,代表來自所有玩家的串流輸入。
為了使重播在 Unity 中可播放,可以將它們以 JSON 格式序列化到QuantumReplayFile類別中,或者建立另一個資料結構來儲存所需的資料。
如何執行重播
Unity
使用QuantumRunnerLocalReplay腳本而不是QuantumRunnerLocalDebug腳本來啟動使用重播的本地遊戲。將記錄的檔案拖放到檢查器的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 焦點。但是 它不適用於重播。
例如,使用 Webhook 在線上捕獲的重播永遠不會有此資訊。
為了支援重播,最好總是將 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 公有雲串流輸入的唯一模式。
RuntimeConfigData以二進位序列化形式(Quantum.AssetSerializer)儲存,就像在重播串流期間接收到的資料一樣。
為了方便起見,重播可能包含一個序列化(AssetSerializer)的資產資料庫AssetDatabaseData,在檔案大小成為問題的生產環境中應該省略此項。
重播可能包含記錄的校驗和,可以作為開發功能在運行時進行驗證。
QuantumJsonFriendlyDataBlob是一個圍繞在 JSON 中儲存二進位資料的包裝器,用於解決 Unity JSON 工具僅詳細序列化位元組陣列的問題。
C#
public class QuantumReplayFile {
// Delta compressed binary input history, this is the same that is send over replay webhooks for example.
public QuantumJsonFriendlyDataBlob InputHistoryDeltaCompressed;
// Full verbose input used in Quantum 2.1, which is still functional, but has only fringe use cases.
public DeterministicTickInputSet[] InputHistoryLegacy;
// Binary serialized RuntimeConfig.
// Use AssetSerializer.ConfigToByteArray(runtimeConfig, compress: true)
/// </summary>
public QuantumJsonFriendlyDataBlob RuntimeConfigData;
/// The session config.
public DeterministicSessionConfig DeterministicConfig;
/// The last tick of the input.
public int LastTick;
/// The initial tick to start from, requires <see cref="InitialFrameData"/> to be set.
public int InitialTick;
/// Optional frame data to start the replay with. This is used for save games for example.
public byte[] InitialFrameData;
/// Optional checksums. Omit this for replays in production environments.
public ChecksumFile Checksums;
/// Optional serialized asset database. Omit this for replays in production environments.
/// Use AssetSerializer.SerializeAssets(stream, ResourceManager.LoadAllAssets().ToArray()
public QuantumJsonFriendlyDataBlob AssetDatabaseData;
}
C#
public class QuantumJsonFriendlyDataBlob {
/// The byte array is saved as is.
public byte[] Binary;
/// The byte array is saved as Base64 text.
public string Base64;
/// Both Binary and Base64 can be GZip compressed.
public bool IsCompressed;
}
即時重播
我們提供了一個腳本,示範如何在輔助的 QuantumRunner 中進行即時重播(例如在遊戲過程中發生的擊殺鏡頭重播),然後在即時重播結束後返回到預設的執行器。
要使用它,只需將名為QuantumInstantReplayDemo的 Unity 元件加入到遊戲物件,進行設定(設定播放速度、重播長度等),然後在遊戲過程中,點擊開始和停止按鈕。