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

相关推荐
前端程序猿i10 分钟前
第 8 篇:Markdown 渲染引擎 —— 从流式解析到安全输出
开发语言·前端·javascript·vue.js·安全
Java小卷11 分钟前
KIE Drools 10.x 规则引擎快速入门
java·后端
coding随想12 分钟前
告别构建焦虑!用 Shoelace 打造零配置的现代 Web 应用
前端
fengsen521131412 分钟前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
竟未曾年少轻狂15 分钟前
Spring Boot 项目集成 Redis
java·spring boot·redis·缓存·消息队列·wpf·redis集群
css趣多多15 分钟前
resize.js
前端·javascript·vue.js
_codemonster23 分钟前
java web修改了文件和新建了文件需要注意的问题
java·开发语言·前端
小冰球27 分钟前
前端侦探:我是如何挖掘出网站里 28 个"隐藏商品"的?
前端·vue.js
Java天梯之路30 分钟前
Spring Boot 钩子全集实战(九):`@PostConstruct` 详解
java·spring boot·后端
松涛和鸣33 分钟前
75、 IMX6ULL LM75温度传感器I2C驱动开发
java·linux·数据库·驱动开发·python