How-To Guide
Common scenarios and step-by-step solutions for Runtime Video Recorder.
Q1: How do I record gameplay with a hotkey?
Scenario: Press F9 to start recording, F10 to stop, and show a notification when done.
Blueprint Solution
// In your Player Controller or Game Mode Blueprint
// Variables
- RecorderRef: Runtime Video Recorder (Object Reference)
- bIsRecording: Boolean = false
// Event BeginPlay
→ Get Engine Subsystem (RuntimeVideoRecorder)
→ Set RecorderRef
// Input Action "StartRecording" (F9)
→ Branch (bIsRecording)
- False:
→ RecorderRef → StartRecording
OutFilename: "%auto%"
TargetFPS: 60
Width: 1920
Height: 1080
EncoderSettings: (Default)
RecordUI: true
EnableAudioRecording: true
→ Set bIsRecording = true
→ Print String "Recording Started"
// Input Action "StopRecording" (F10)
→ Branch (bIsRecording)
- True:
→ RecorderRef → StopRecording
→ Set bIsRecording = false
→ RecorderRef → Get Last Recording Filepath
→ Print String ("Video saved: " + Filepath)C++ Solution
// MyPlayerController.h
UCLASS()
class AMyPlayerController : public APlayerController
{
GENERATED_BODY()
protected:
virtual void SetupInputComponent() override;
private:
void ToggleRecording();
UPROPERTY()
URuntimeVideoRecorder* Recorder;
};
// MyPlayerController.cpp
void AMyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAction("ToggleRecording", IE_Pressed, this, &AMyPlayerController::ToggleRecording);
}
void AMyPlayerController::ToggleRecording()
{
if (!Recorder)
{
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
}
if (!Recorder) return;
if (Recorder->IsRecordingInProgress())
{
// Stop recording
Recorder->StopRecording_NativeAPI();
FString Path = Recorder->GetLastRecordingFilepath();
UE_LOG(LogTemp, Log, TEXT("Video saved: %s"), *Path);
// Show notification
if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
{
PC->ClientMessage(FString::Printf(TEXT("Video saved: %s"), *FPaths::GetCleanFilename(Path)));
}
}
else
{
// Start recording
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 20000000;
Settings.TargetQuality = 75;
bool bSuccess = Recorder->StartRecording(
TEXT("%auto%"),
60, 1920, 1080,
Settings,
true, true
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Recording started"));
}
}
}Q2: How do I record only the last 30 seconds (instant replay)?
Scenario: Continuously record the last 30 seconds. When player presses a key, save those 30 seconds.
Solution
// Start circular buffer recording
void AGameMode::BeginPlay()
{
Super::BeginPlay();
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 20000000;
// LastSecondsToRecord = 30.0f enables circular buffer
Recorder->StartRecording(
TEXT("%auto%"),
30, // 30 FPS
1920, 1080,
Settings,
true, // Record UI
true, // Enable audio
false, // Frame rate dependent
false, // Auto capture
30.0f, // ← Circular buffer: last 30 seconds
false, // On-the-fly encoding
nullptr // All audio
);
UE_LOG(LogTemp, Log, TEXT("Circular buffer recording started (last 30 seconds)"));
}
}
// Save replay when player does something cool
void AMyCharacter::OnKillEnemy()
{
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder && Recorder->IsRecordingInProgress())
{
// Stop recording - this saves the last 30 seconds
Recorder->StopRecording_NativeAPI();
UE_LOG(LogTemp, Log, TEXT("Saved last 30 seconds as replay!"));
// Start recording again for next replay
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]()
{
// Restart circular buffer recording after previous one finishes
AGameMode* GM = Cast<AGameMode>(GetWorld()->GetAuthGameMode());
if (GM)
{
GM->BeginPlay(); // Restart recording
}
}, 2.0f, false);
}
}Key Point: With LastSecondsToRecord = 30.0f, RVR continuously records but only keeps the most recent 30 seconds in memory. When you stop, it saves those 30 seconds.
Q3: How do I record a security camera view in-game?
Scenario: Record from a specific camera actor placed in the level (like a CCTV camera).
Solution
// SecurityCamera.h
UCLASS()
class ASecurityCamera : public AActor
{
GENERATED_BODY()
public:
ASecurityCamera();
UFUNCTION(BlueprintCallable, Category = "Security")
void StartRecording();
UFUNCTION(BlueprintCallable, Category = "Security")
void StopRecording();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
class UCameraComponent* CameraComponent;
UPROPERTY()
URuntimeVideoRecorder* Recorder;
};
// SecurityCamera.cpp
ASecurityCamera::ASecurityCamera()
{
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
CameraComponent->SetupAttachment(RootComponent);
CameraComponent->SetFieldOfView(90.0f);
}
void ASecurityCamera::StartRecording()
{
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder && CameraComponent)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 8000000; // Lower quality for security footage
Settings.TargetQuality = 50;
// Record from this camera's perspective
bool bSuccess = Recorder->StartRecordingCamera(
CameraComponent,
TEXT("E:/SecurityFootage/camera_") + GetName() + TEXT(".mp4"),
30, // 30 FPS
1280, 720, // 720p is enough for security
Settings,
false, // No audio needed
false, // Frame rate dependent
false, // Auto capture
-1.0f, // No circular buffer
false, // On-the-fly encoding
nullptr, // No audio
100.0f // Full quality
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Security camera %s started recording"), *GetName());
}
}
}
void ASecurityCamera::StopRecording()
{
if (Recorder && Recorder->IsRecordingInProgress())
{
Recorder->StopRecording_NativeAPI();
FString Path = Recorder->GetLastRecordingFilepath();
UE_LOG(LogTemp, Log, TEXT("Security footage saved: %s"), *Path);
}
}Usage in Level:
- Place
ASecurityCameraactors in your level - Call
StartRecording()when level starts - Call
StopRecording()when level ends or on demand
Q4: How do I create a "photo mode" that captures screenshots and videos?
Scenario: Press P to enter photo mode, then take screenshots or record videos with custom camera controls.
Solution
// PhotoModeController.h
UCLASS()
class APhotoModeController : public AActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "PhotoMode")
void EnterPhotoMode();
UFUNCTION(BlueprintCallable, Category = "PhotoMode")
void ExitPhotoMode();
UFUNCTION(BlueprintCallable, Category = "PhotoMode")
void TakeScreenshot();
UFUNCTION(BlueprintCallable, Category = "PhotoMode")
void ToggleVideoRecording();
protected:
UPROPERTY()
UCameraComponent* PhotoCamera;
UPROPERTY()
URuntimeVideoRecorder* Recorder;
UPROPERTY()
APlayerController* PlayerController;
bool bInPhotoMode = false;
int32 ScreenshotCounter = 0;
};
// PhotoModeController.cpp
void APhotoModeController::EnterPhotoMode()
{
bInPhotoMode = true;
PlayerController = GetWorld()->GetFirstPlayerController();
if (!PlayerController) return;
// Create photo camera
PhotoCamera = NewObject<UCameraComponent>(this);
PhotoCamera->RegisterComponent();
// Copy player camera transform
FVector CamLoc;
FRotator CamRot;
PlayerController->GetPlayerViewPoint(CamLoc, CamRot);
PhotoCamera->SetWorldLocationAndRotation(CamLoc, CamRot);
// Pause game
UGameplayStatics::SetGamePaused(GetWorld(), true);
// Hide player pawn
if (APawn* PlayerPawn = PlayerController->GetPawn())
{
PlayerPawn->SetActorHiddenInGame(true);
}
UE_LOG(LogTemp, Log, TEXT("Photo Mode enabled"));
}
void APhotoModeController::ExitPhotoMode()
{
bInPhotoMode = false;
// Stop recording if active
if (Recorder && Recorder->IsRecordingInProgress())
{
Recorder->StopRecording_NativeAPI();
}
// Unpause game
UGameplayStatics::SetGamePaused(GetWorld(), false);
// Show player pawn
if (PlayerController)
{
if (APawn* PlayerPawn = PlayerController->GetPawn())
{
PlayerPawn->SetActorHiddenInGame(false);
}
}
UE_LOG(LogTemp, Log, TEXT("Photo Mode disabled"));
}
void APhotoModeController::TakeScreenshot()
{
if (!bInPhotoMode) return;
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
FString Filename = FString::Printf(
TEXT("E:/Screenshots/photo_%03d.png"),
ScreenshotCounter++
);
Recorder->MakeScreenshot(Filename, false); // Don't show UI
UE_LOG(LogTemp, Log, TEXT("Screenshot saved: %s"), *Filename);
}
}
void APhotoModeController::ToggleVideoRecording()
{
if (!bInPhotoMode || !PhotoCamera) return;
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (!Recorder) return;
if (Recorder->IsRecordingInProgress())
{
// Stop recording
Recorder->StopRecording_NativeAPI();
UE_LOG(LogTemp, Log, TEXT("Photo mode video stopped"));
}
else
{
// Start recording from photo camera
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 30000000; // High quality
Settings.TargetQuality = 85;
Settings.Profile = ERuntimeEncoderProfile::Profile_High;
Recorder->StartRecordingCamera(
PhotoCamera,
TEXT("%auto%"),
60, 3840, 2160, // 4K 60fps
Settings,
false // No audio in photo mode
);
UE_LOG(LogTemp, Log, TEXT("Photo mode video started"));
}
}Q5: How do I record a time-lapse (1 frame per second)?
Scenario: Record 1 frame every second for a 30-minute gameplay session, resulting in a 30-second time-lapse video.
Solution
void ATimeLapseRecorder::StartTimeLapse()
{
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 15000000;
Settings.TargetQuality = 70;
// Start in MANUAL capture mode
bool bSuccess = Recorder->StartRecording(
TEXT("E:/TimeLapse/timelapse.mp4"),
30, // Output will be 30 FPS
1920, 1080,
Settings,
false, // No UI
false, // No audio for timelapse
false, // Frame rate dependent
true, // ← MANUAL CAPTURE ONLY!
-1.0f, // No circular buffer
false, // On-the-fly encoding
nullptr
);
if (bSuccess)
{
// Set up timer to capture 1 frame per second
GetWorld()->GetTimerManager().SetTimer(
CaptureTimerHandle,
this,
&ATimeLapseRecorder::CaptureFrame,
1.0f, // Every 1 second
true // Looping
);
UE_LOG(LogTemp, Log, TEXT("Time-lapse recording started"));
}
}
}
void ATimeLapseRecorder::CaptureFrame()
{
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder && Recorder->IsRecordingInProgress())
{
Recorder->CaptureSingleFrame();
FramesCaptured++;
// Optional: Log progress
if (FramesCaptured % 60 == 0)
{
UE_LOG(LogTemp, Log, TEXT("Time-lapse: %d frames captured"), FramesCaptured);
}
}
}
void ATimeLapseRecorder::StopTimeLapse()
{
GetWorld()->GetTimerManager().ClearTimer(CaptureTimerHandle);
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder && Recorder->IsRecordingInProgress())
{
Recorder->StopRecording_NativeAPI();
FString Path = Recorder->GetLastRecordingFilepath();
UE_LOG(LogTemp, Log, TEXT("Time-lapse saved: %s (%d frames)"), *Path, FramesCaptured);
}
}Result: 1800 seconds (30 minutes) captured at 1 frame/sec = 1800 frames. At 30 FPS output = 60 second video showing 30 minutes in 1 minute!
Q6: How do I record multiple camera angles simultaneously?
Scenario: Record a split-screen video showing 4 different camera angles at once (e.g., front, back, left, right).
Solution
void AMultiCameraRecorder::StartRecording()
{
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
// Get all camera components
TArray<UCameraComponent*> Cameras;
// Assuming you have these cameras set up
if (FrontCamera) Cameras.Add(FrontCamera);
if (BackCamera) Cameras.Add(BackCamera);
if (LeftCamera) Cameras.Add(LeftCamera);
if (RightCamera) Cameras.Add(RightCamera);
if (Cameras.Num() > 0)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 30000000; // Higher bitrate for multiple views
Settings.TargetQuality = 75;
// Record all cameras in grid layout (2x2 for 4 cameras)
bool bSuccess = Recorder->StartRecordingMultipleCameras(
Cameras,
TEXT("E:/Replays/multicam.mp4"),
30, // 30 FPS
1920, 1080, // Total resolution (each camera will be 960x540)
Settings,
true, // Enable audio
false, // Frame rate dependent
false, // Auto capture
-1.0f, // No circular buffer
false, // On-the-fly encoding
nullptr, // All audio
100.0f // Full quality
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Multi-camera recording started with %d cameras"), Cameras.Num());
}
}
}
}Camera Setup in Level:
void AMultiCameraRecorder::BeginPlay()
{
Super::BeginPlay();
// Create cameras around the player/subject
APawn* PlayerPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
if (PlayerPawn)
{
FVector PlayerLoc = PlayerPawn->GetActorLocation();
// Front camera
FrontCamera = NewObject<UCameraComponent>(this);
FrontCamera->RegisterComponent();
FrontCamera->SetWorldLocation(PlayerLoc + FVector(300, 0, 100));
FrontCamera->SetWorldRotation(FRotator(0, 180, 0));
// Back camera
BackCamera = NewObject<UCameraComponent>(this);
BackCamera->RegisterComponent();
BackCamera->SetWorldLocation(PlayerLoc + FVector(-300, 0, 100));
BackCamera->SetWorldRotation(FRotator(0, 0, 0));
// Left camera
LeftCamera = NewObject<UCameraComponent>(this);
LeftCamera->RegisterComponent();
LeftCamera->SetWorldLocation(PlayerLoc + FVector(0, -300, 100));
LeftCamera->SetWorldRotation(FRotator(0, 90, 0));
// Right camera
RightCamera = NewObject<UCameraComponent>(this);
RightCamera->RegisterComponent();
RightCamera->SetWorldLocation(PlayerLoc + FVector(0, 300, 100));
RightCamera->SetWorldRotation(FRotator(0, -90, 0));
}
}Q7: How do I record only music/sound effects without voice chat?
Scenario: In a multiplayer game, record gameplay audio but exclude voice chat from the recording.
Solution
void AGameRecorder::StartRecordingWithoutVoiceChat()
{
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
// Get the master submix (everything except voice chat)
// Assuming voice chat uses a separate submix
USoundSubmix* MasterSubmix = LoadObject<USoundSubmix>(
nullptr,
TEXT("/Game/Audio/MasterSubmix")
);
if (MasterSubmix)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 20000000;
Settings.TargetQuality = 75;
// Record with specific submix only
bool bSuccess = Recorder->StartRecording(
TEXT("%auto%"),
60, 1920, 1080,
Settings,
true, // Record UI
true, // Enable audio
false, // Frame rate dependent
false, // Auto capture
-1.0f, // No circular buffer
false, // On-the-fly encoding
MasterSubmix // ← Only record this submix (no voice chat)
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Recording started (music & SFX only, no voice chat)"));
}
}
}
}Audio Setup Required:
Create submixes:
MasterSubmix- For music and sound effectsVoiceChatSubmix- For voice chat only
Route audio appropriately:
cpp// Route game sounds to MasterSubmix GameSound->SetSubmixSend(MasterSubmix); // Route voice chat to VoiceChatSubmix VoiceChatSound->SetSubmixSend(VoiceChatSubmix);
Q8: How do I automatically record boss fights?
Scenario: Automatically start recording when a boss fight begins and stop when it ends.
Solution
// BossFightRecorder.h
UCLASS()
class ABossFightRecorder : public AActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Recording")
void OnBossFightStart(const FString& BossName);
UFUNCTION(BlueprintCallable, Category = "Recording")
void OnBossFightEnd(bool bPlayerWon);
private:
UPROPERTY()
URuntimeVideoRecorder* Recorder;
FString CurrentBossName;
FDateTime RecordingStartTime;
};
// BossFightRecorder.cpp
void ABossFightRecorder::OnBossFightStart(const FString& BossName)
{
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder && !Recorder->IsRecordingInProgress())
{
CurrentBossName = BossName;
RecordingStartTime = FDateTime::Now();
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 25000000; // High quality for boss fights
Settings.TargetQuality = 80;
Settings.Profile = ERuntimeEncoderProfile::Profile_High;
// Generate filename with boss name and timestamp
FString Filename = FString::Printf(
TEXT("E:/BossFights/%s_%s.mp4"),
*BossName.Replace(TEXT(" "), TEXT("_")),
*RecordingStartTime.ToString(TEXT("%Y%m%d_%H%M%S"))
);
bool bSuccess = Recorder->StartRecording(
Filename,
60, // 60 FPS for epic boss fights
1920, 1080,
Settings,
true, // Record UI (health bars, etc.)
true // Enable audio (boss music!)
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Started recording boss fight: %s"), *BossName);
// Optional: Show recording indicator
if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
{
PC->ClientMessage(FString::Printf(TEXT("Recording: %s"), *BossName));
}
}
}
}
void ABossFightRecorder::OnBossFightEnd(bool bPlayerWon)
{
if (Recorder && Recorder->IsRecordingInProgress())
{
Recorder->StopRecording_NativeAPI();
FString Result = bPlayerWon ? TEXT("Victory") : TEXT("Defeat");
FString Path = Recorder->GetLastRecordingFilepath();
UE_LOG(LogTemp, Log, TEXT("Boss fight recording complete: %s - %s"), *CurrentBossName, *Result);
UE_LOG(LogTemp, Log, TEXT("Saved to: %s"), *Path);
// Optional: Add result to filename
FString NewPath = Path.Replace(TEXT(".mp4"), *FString::Printf(TEXT("_%s.mp4"), *Result));
IFileManager::Get().Move(*NewPath, *Path);
// Show notification
if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
{
PC->ClientMessage(FString::Printf(TEXT("Boss fight saved: %s"), *Result));
}
}
}Usage in Boss Blueprint:
// Boss Actor Blueprint
// Event: Boss Health <= 0
→ Cast to BossFightRecorder
→ OnBossFightEnd (bPlayerWon = true)
// Event: Player Health <= 0
→ Cast to BossFightRecorder
→ OnBossFightEnd (bPlayerWon = false)
// Event: Boss Spawned
→ Cast to BossFightRecorder
→ OnBossFightStart (BossName = "Dragon King")Q9: How do I record a render target for a minimap replay?
Scenario: Record a top-down minimap view to a separate video file for later analysis.
Solution
void AMinimapRecorder::StartMinimapRecording()
{
// Create render target for minimap
MinimapRenderTarget = NewObject<UTextureRenderTarget2D>();
MinimapRenderTarget->InitAutoFormat(512, 512); // 512x512 minimap
MinimapRenderTarget->ClearColor = FLinearColor::Black;
// Create minimap camera
MinimapCamera = NewObject<UCameraComponent>(this);
MinimapCamera->RegisterComponent();
MinimapCamera->ProjectionMode = ECameraProjectionMode::Orthographic;
MinimapCamera->OrthoWidth = 5000.0f; // View width in world units
// Position camera above the map
APawn* PlayerPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
if (PlayerPawn)
{
FVector PlayerLoc = PlayerPawn->GetActorLocation();
MinimapCamera->SetWorldLocation(FVector(PlayerLoc.X, PlayerLoc.Y, PlayerLoc.Z + 2000.0f));
MinimapCamera->SetWorldRotation(FRotator(-90, 0, 0)); // Look straight down
}
// Capture scene to render target
USceneCaptureComponent2D* SceneCapture = NewObject<USceneCaptureComponent2D>(this);
SceneCapture->RegisterComponent();
SceneCapture->TextureTarget = MinimapRenderTarget;
SceneCapture->ProjectionType = ECameraProjectionMode::Orthographic;
SceneCapture->OrthoWidth = 5000.0f;
SceneCapture->SetWorldLocation(MinimapCamera->GetComponentLocation());
SceneCapture->SetWorldRotation(MinimapCamera->GetComponentRotation());
// Start recording the render target
URuntimeVideoRecorder* Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 5000000; // Lower bitrate for small resolution
Settings.TargetQuality = 60;
bool bSuccess = Recorder->StartRecordingRenderTarget(
MinimapRenderTarget,
TEXT("E:/Replays/minimap.mp4"),
30, // 30 FPS is enough
512, 512, // Match render target size
Settings,
false // No audio for minimap
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Minimap recording started"));
// Update minimap position every frame
GetWorld()->GetTimerManager().SetTimer(
UpdateTimerHandle,
this,
&AMinimapRecorder::UpdateMinimapPosition,
0.033f, // ~30 FPS
true
);
}
}
}
void AMinimapRecorder::UpdateMinimapPosition()
{
// Follow player
APawn* PlayerPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
if (PlayerPawn && MinimapCamera)
{
FVector PlayerLoc = PlayerPawn->GetActorLocation();
MinimapCamera->SetWorldLocation(FVector(PlayerLoc.X, PlayerLoc.Y, PlayerLoc.Z + 2000.0f));
}
}Q10: How do I create a crash recovery system?
Scenario: If the game crashes, save the last 60 seconds of gameplay automatically on next launch.
Solution
// CrashRecoverySystem.h
UCLASS()
class ACrashRecoverySystem : public AActor
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
void CheckForCrashRecovery();
void StartContinuousRecording();
void WriteHeartbeat();
UPROPERTY()
URuntimeVideoRecorder* Recorder;
FTimerHandle HeartbeatTimer;
FString HeartbeatFilePath;
};
// CrashRecoverySystem.cpp
void ACrashRecoverySystem::BeginPlay()
{
Super::BeginPlay();
HeartbeatFilePath = FPaths::ProjectSavedDir() / TEXT("heartbeat.txt");
// Check if previous session crashed
CheckForCrashRecovery();
// Start continuous recording with circular buffer
StartContinuousRecording();
// Write heartbeat every second
GetWorld()->GetTimerManager().SetTimer(
HeartbeatTimer,
this,
&ACrashRecoverySystem::WriteHeartbeat,
1.0f,
true
);
}
void ACrashRecoverySystem::CheckForCrashRecovery()
{
// Check if heartbeat file exists (indicates crash)
if (FPaths::FileExists(HeartbeatFilePath))
{
UE_LOG(LogTemp, Warning, TEXT("Previous session crashed! Attempting to recover video..."));
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
// Try to encode circular buffer from crashed session
FString RecoveryPath = FPaths::ProjectSavedDir() / TEXT("CrashRecovery");
bool bSuccess = Recorder->EncodeCircularBufferToVideo(RecoveryPath);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Crash recovery video saved!"));
// Show notification to player
if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
{
PC->ClientMessage(TEXT("Previous session crashed. Last 60 seconds recovered."));
}
}
}
// Delete heartbeat file
IFileManager::Get().Delete(*HeartbeatFilePath);
}
}
void ACrashRecoverySystem::StartContinuousRecording()
{
Recorder = GEngine->GetEngineSubsystem<URuntimeVideoRecorder>();
if (Recorder)
{
FRuntimeEncoderSettings Settings;
Settings.VideoBitrate = 15000000;
Settings.TargetQuality = 60;
// Start recording with 60-second circular buffer
bool bSuccess = Recorder->StartRecording(
TEXT("%auto%"),
30, 1920, 1080,
Settings,
true, // Record UI
true, // Enable audio
false, // Frame rate dependent
false, // Auto capture
60.0f, // ← 60 second circular buffer
false, // On-the-fly encoding
nullptr
);
if (bSuccess)
{
UE_LOG(LogTemp, Log, TEXT("Crash recovery system active (60s buffer)"));
}
}
}
void ACrashRecoverySystem::WriteHeartbeat()
{
// Write current time to file (proves game is still running)
FString TimeStamp = FDateTime::Now().ToString();
FFileHelper::SaveStringToFile(TimeStamp, *HeartbeatFilePath);
}
void ACrashRecoverySystem::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Normal shutdown - delete heartbeat file
if (FPaths::FileExists(HeartbeatFilePath))
{
IFileManager::Get().Delete(*HeartbeatFilePath);
}
// Stop recording
if (Recorder && Recorder->IsRecordingInProgress())
{
Recorder->StopRecording_NativeAPI();
}
// Clear timer
GetWorld()->GetTimerManager().ClearTimer(HeartbeatTimer);
Super::EndPlay(EndPlayReason);
}How it works:
- On startup, check for "heartbeat" file from previous session
- If exists = previous session crashed without cleanup
- Call
EncodeCircularBufferToVideo()to save last 60 seconds - During gameplay, continuously record last 60 seconds in memory
- Write heartbeat file every second (proves game is alive)
- On normal shutdown, delete heartbeat file
- If crash occurs, heartbeat file remains → recovery on next launch
See Also
- Quick Start - Basic recording examples
- API Reference - Complete method documentation
- Troubleshooting - Common issues and solutions
- Project Settings - Global configuration options
