这是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
// 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
// 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 functionOnEnumerateStreamsCompleteDelegate1 = FEnumerateStreamsCallback::CreateUObject(this, &UMyGameInstance::OnEnumerateStreamsComplete1);// Link DeleteReplay() delegate to functionOnDeleteFinishedStreamCompleteDelegate1 = 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 InfoFNullReplayInfo 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 FriendlyNameInfo.FriendlyName = NewFriendlyReplayName;// Write File InfoTUniquePtr<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
// 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
// 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 bothstatic 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 FXAACVarAA->Set(1);CVarMB->Set(0);WorldSettings->SetPauserPlayerState(PlayerState);return true;}// Rest MotionBlur and AACVarAA->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