UE5.4.3 录屏回放系统ReplaySystem蓝图版

这是ReplaySystem的蓝图使用方法版,以第三人称模版为例,需要几个必须步骤

项目config内DefaultEngine.ini的最后添加:

复制代码
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="DemoNetDriver",DriverClassName="/Script/Engine.DemoNetDriver",DriverClassNameFallback="/Script/Engine.DemoNetDriver")

项目的.build.cs用添加Josn模块

复制代码
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput","Json" });

创建MyGameInstance继承至GameInstance

MyGameInstance.h

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"

#include "NetworkReplayStreaming.h"
#include "Runtime/NetworkReplayStreaming/NullNetworkReplayStreaming/Public/NullNetworkReplayStreaming.h"
#include "Misc/NetworkVersion.h"

#include "MyGameInstance.generated.h"

USTRUCT(BlueprintType)
struct FS_ReplayInfo
{
	GENERATED_USTRUCT_BODY()
 
	UPROPERTY(BlueprintReadOnly)
	FString ReplayName;
	UPROPERTY(BlueprintReadOnly)
	FString FriendlyName;
	UPROPERTY(BlueprintReadOnly)
	FDateTime Timestamp;
	UPROPERTY(BlueprintReadOnly)
	int32 LengthInMS;
	UPROPERTY(BlueprintReadOnly)
	bool bIsValid;
 
	FS_ReplayInfo()
	{
		ReplayName = "Replay";
		FriendlyName = "Replay";
		Timestamp = FDateTime::MinValue();
		LengthInMS = 0;
		bIsValid = false;
	}
 
	FS_ReplayInfo(FString NewName, FString NewFriendlyName, FDateTime NewTimestamp, int32 NewLengthInMS)
	{
		ReplayName = NewName;
		FriendlyName = NewFriendlyName;
		Timestamp = NewTimestamp;
		LengthInMS = NewLengthInMS;
		bIsValid = true;
	}
};

UCLASS()
class REPLAYUE54_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintCallable, Category = "Replays")
	void StartRecordingReplayFromBP(FString ReplayName, FString FriendlyName);
	UFUNCTION(BlueprintCallable, Category = "Replays")
	void StopRecordingReplayFromBP();
	UFUNCTION(BlueprintCallable, Category = "Replays")
	void PlayReplayFromBP(FString ReplayName);
	UFUNCTION(BlueprintCallable, Category = "Replays")
	void FindReplays();
	UFUNCTION(BlueprintCallable, Category = "Replays")
	void RenameReplay(const FString& ReplayName, const FString& NewFriendlyReplayName);
	UFUNCTION(BlueprintCallable, Category = "Replays")
	void DeleteReplay(const FString& ReplayName);
 
	virtual void Init() override;
 
	TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr;
 
	FEnumerateStreamsCallback OnEnumerateStreamsCompleteDelegate1;
	void OnEnumerateStreamsComplete1(const FEnumerateStreamsResult& Result);
 
	FDeleteFinishedStreamCallback OnDeleteFinishedStreamCompleteDelegate1;
	void OnDeleteFinishedStreamComplete1(const FDeleteFinishedStreamResult& Result);
 
	UFUNCTION(BlueprintImplementableEvent, Category = "Replays")
	void BP_OnFindReplaysComplete1(const TArray<FS_ReplayInfo>& AllReplaysm);	
	
};

MyGmaeInstance.cpp

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameInstance.h"

#include "Modules/ModuleManager.h"
#include "Runtime/Core/Public/HAL/FileManager.h"
#include "Runtime/Core/Public/Misc/FileHelper.h"

void UMyGameInstance::Init()
{
	Super::Init();
 
	// create a ReplayStreamer for FindReplays() and DeleteReplay(..)
	EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();

	// Link FindReplays() delegate to function
	OnEnumerateStreamsCompleteDelegate1 = FEnumerateStreamsCallback::CreateUObject(this, &UMyGameInstance::OnEnumerateStreamsComplete1);

	// Link DeleteReplay() delegate to function
	OnDeleteFinishedStreamCompleteDelegate1 = FDeleteFinishedStreamCallback::CreateUObject(this, &UMyGameInstance::OnDeleteFinishedStreamComplete1);
}
void UMyGameInstance::StartRecordingReplayFromBP(FString ReplayName, FString FriendlyName)
{
	StartRecordingReplay(ReplayName, FriendlyName);
}
 
void UMyGameInstance::StopRecordingReplayFromBP()
{
	StopRecordingReplay();
}
 
void UMyGameInstance::PlayReplayFromBP(FString ReplayName)
{
	PlayReplay(ReplayName);
}
void UMyGameInstance::FindReplays()
{
	if (EnumerateStreamsPtr.Get())
	{
		EnumerateStreamsPtr.Get()->EnumerateStreams(FNetworkReplayVersion(), int32(), FString(), TArray<FString>(), OnEnumerateStreamsCompleteDelegate1);
	}
}
 
void UMyGameInstance::OnEnumerateStreamsComplete1(const FEnumerateStreamsResult& Result)
{
	TArray<FS_ReplayInfo> AllReplays;
 
	for (FNetworkReplayStreamInfo StreamInfo : Result.FoundStreams)
	{
		void BP_OnFindReplaysComplete1(const TArray<FS_ReplayInfo> &AllReplaysm);
		if (!StreamInfo.bIsLive)
		{
			AllReplays.Add(FS_ReplayInfo(StreamInfo.Name, StreamInfo.FriendlyName, StreamInfo.Timestamp, StreamInfo.LengthInMS));
		}
	}
	BP_OnFindReplaysComplete1(AllReplays);
}
 
void UMyGameInstance::RenameReplay(const FString& ReplayName, const FString& NewFriendlyReplayName)
{
	// Get File Info
	FNullReplayInfo Info;
 
	const FString DemoPath = FPaths::Combine(*FPaths::ProjectSavedDir(), TEXT("Demos/"));
	const FString StreamDirectory = FPaths::Combine(*DemoPath, *ReplayName);
	const FString StreamFullBaseFilename = FPaths::Combine(*StreamDirectory, *ReplayName);
	const FString InfoFilename = StreamFullBaseFilename + TEXT(".replayinfo");
 
	TUniquePtr<FArchive> InfoFileArchive(IFileManager::Get().CreateFileReader(*InfoFilename));
 
	if (InfoFileArchive.IsValid() && InfoFileArchive->TotalSize() != 0)
	{
		FString JsonString;
		*InfoFileArchive << JsonString;
 
		Info.FromJson(JsonString);
		Info.bIsValid = true;
 
		InfoFileArchive->Close();
	}
 
	// Set FriendlyName
	Info.FriendlyName = NewFriendlyReplayName;
 
	// Write File Info
	TUniquePtr<FArchive> ReplayInfoFileAr(IFileManager::Get().CreateFileWriter(*InfoFilename));
 
	if (ReplayInfoFileAr.IsValid())
	{
		FString JsonString = Info.ToJson();
		*ReplayInfoFileAr << JsonString;
 
		ReplayInfoFileAr->Close();
	}
}
void UMyGameInstance::DeleteReplay(const FString& ReplayName)
{
	if (EnumerateStreamsPtr.Get())
	{
		EnumerateStreamsPtr.Get()->DeleteFinishedStream(ReplayName, OnDeleteFinishedStreamCompleteDelegate1);
	}
}
 
void UMyGameInstance::OnDeleteFinishedStreamComplete1(const FDeleteFinishedStreamResult& Result)
{
	FindReplays();
}

创建ReplayControllerplayer类继承至PlayerController类

复制代码
ReplayControllerplayer.h
cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ReplayControllerplayer.generated.h"

/**
 * 
 */
UCLASS()
class REPLAYUE54_API AReplayControllerplayer : public APlayerController
{
	GENERATED_BODY()

public:

	/** we must set some Pause-Behavior values in the ctor */
	AReplayControllerplayer(const FObjectInitializer& ObjectInitializer);
 
protected:
 
	/** for saving Anti-Aliasing and Motion-Blur settings during Pause State */
	int32 PreviousAASetting;
	int32 PreviousMBSetting;
 
public:
 
	/** Set the Paused State of the Running Replay to bDoPause. Return new Pause State */
	UFUNCTION(BlueprintCallable, Category = "CurrentReplay")
	bool SetCurrentReplayPausedState(bool bDoPause);
 
	/** Gets the Max Number of Seconds that were recorded in the current Replay */
	UFUNCTION(BlueprintCallable, Category = "CurrentReplay")
	int32 GetCurrentReplayTotalTimeInSeconds() const;
 
	/** Gets the Second we are currently watching in the Replay */
	UFUNCTION(BlueprintCallable, Category = "CurrentReplay")
	int32 GetCurrentReplayCurrentTimeInSeconds() const;
 
	/** Jumps to the specified Second in the Replay we are watching */
	UFUNCTION(BlueprintCallable, Category = "CurrentReplay")
	void SetCurrentReplayTimeToSeconds(int32 Seconds);
 
	/** Changes the PlayRate of the Replay we are watching, enabling FastForward or SlowMotion */
	UFUNCTION(BlueprintCallable, Category = "CurrentReplay")
	void SetCurrentReplayPlayRate(float PlayRate = 1.f);
};

ReplayControllerplayer.cpp

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "ReplayControllerplayer.h"
#include "Engine/World.h"
#include "Engine/DemoNetDriver.h"

AReplayControllerplayer::AReplayControllerplayer(const FObjectInitializer& ObjectInitializer)
{
	bShowMouseCursor = true;
	PrimaryActorTick.bTickEvenWhenPaused = true;
	bShouldPerformFullTickWhenPaused = true;
}

bool AReplayControllerplayer::SetCurrentReplayPausedState(bool bDoPause)
{
	AWorldSettings* WorldSettings = GetWorldSettings();
 
	// Set MotionBlur off and Anti Aliasing to FXAA in order to bypass the pause-bug of both
	static const auto CVarAA = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DefaultFeature.AntiAliasing"));
 
	static const auto CVarMB = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DefaultFeature.MotionBlur"));
 
	if (bDoPause)
	{
		PreviousAASetting = CVarAA->GetInt();
		PreviousMBSetting = CVarMB->GetInt();
 
		// Set MotionBlur to OFF, Anti-Aliasing to FXAA
		CVarAA->Set(1);
		CVarMB->Set(0);
 
		WorldSettings->SetPauserPlayerState(PlayerState);
		return true;
	}
	// Rest MotionBlur and AA
	CVarAA->Set(PreviousAASetting);
	CVarMB->Set(PreviousMBSetting);
 
	WorldSettings->SetPauserPlayerState(NULL);
	return false;
}

int32 AReplayControllerplayer::GetCurrentReplayTotalTimeInSeconds() const
{
	if (GetWorld())
	{
		if (UDemoNetDriver* DemoNetDriver = GetWorld()->GetDemoNetDriver())
		{
			return DemoNetDriver->GetDemoTotalTime();
		}
	}
	return 0;
}

int32 AReplayControllerplayer::GetCurrentReplayCurrentTimeInSeconds() const
{
	if (GetWorld())
	{
		if (UDemoNetDriver* DemoNetDriver = GetWorld()->GetDemoNetDriver())
		{
			return DemoNetDriver->GetDemoCurrentTime();
		}
	}
	return 0;
}

void AReplayControllerplayer::SetCurrentReplayTimeToSeconds(int32 Seconds)
{
	if (GetWorld())
	{
		if (UDemoNetDriver* DemoNetDriver = GetWorld()->GetDemoNetDriver())
		{
			DemoNetDriver->GotoTimeInSeconds(Seconds);
		}
	}
}

void AReplayControllerplayer::SetCurrentReplayPlayRate(float PlayRate)
{
	if (GetWorld())
	{
		if (UDemoNetDriver* DemoNetDriver = GetWorld()->GetDemoNetDriver())
		{
			GetWorld()->GetWorldSettings()->DemoPlayTimeDilation = PlayRate;
		}
	}
}

创建MyGmaeInstance与ReplayController的BP蓝图类

UserInterface类:ReplayChild,ReplayMenu,ReplaySpectator

项目设置中

BP_MyGameInstance

BP_ReplayController

BP_ThirdPersonGameMode中的设置

ReplayChild

ReplayMenu

ReplaySpectator

相关推荐
君顾12 分钟前
西安 24 小时自助健身房系统开发实战指南
java·开发语言·健身房
用户094248568032 分钟前
第9章:OpenJDK堆分代与 Serial/Parallel GC 基础
java·jvm
Wang's Blog11 分钟前
Java框架快速入门: Spring Security+OAuth2之RBAC与角色分层实践
java·网络·spring
caoerzhong23 分钟前
JeeWMS 开源仓库管理系统多租户架构解析:一套 Java WMS 如何同时服务多个货主与多个仓库
java·架构·开源·vue
sunshine22 girl29 分钟前
Java学习四 面向对象原理
java
干到60岁退休的码农31 分钟前
15.创建获取用户接口与JWT认证器
java·spring boot·mybatis
小羊没烦恼!37 分钟前
Memory 记忆设计讨论:Agent Memory 的数据模型可以怎么设计
java·开发语言·前端·c++·c#
SL_staff1 小时前
战略落地的最后一公里:从目标到个人日历的自动化链路实现
java·github·全栈
用户3721574261351 小时前
Java 设置 Excel 数据验证:整数、日期、文本长度、下拉列表和时间
java
码上成长1 小时前
Mapbox 围栏编辑踩过的几个坑:key 串了、形状炸了、icon 挡住绿点
前端·前端框架