前言:UE4.26的回放教程,最近有用到,So梳理了整个构建流程,希望能帮到你!(结尾有视频版教程,时长较长)


1.准备工作:

  • 创建一个UE4C++项目,添加第一人称和第三人称功能包;

  • 关闭引擎,找到项目目录:../ContentDir/Config/DefaultEngine.ini添加如下代码:

[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="DemoNetDriver",DriverClassName="/Script/Engine.DemoNetDriver",DriverClassNameFallback="/Script/Engine.DemoNetDriver")
  • 重新打开项目。创建新的C++类MyGameInstance继承自:GameInstance

  • XXX.Build.cs中添加模块:Json

    PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore","Json"});
  • MyGameInstance.h中:

#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_ReplayInfo1
{GENERATED_USTRUCT_BODY()UPROPERTY(BlueprintReadOnly)FString ReplayName;UPROPERTY(BlueprintReadOnly)FString FriendlyName;UPROPERTY(BlueprintReadOnly)FDateTime Timestamp;UPROPERTY(BlueprintReadOnly)int32 LengthInMS;UPROPERTY(BlueprintReadOnly)bool bIsValid;FS_ReplayInfo1(){ReplayName = "Replay";FriendlyName = "Replay";Timestamp = FDateTime::MinValue();LengthInMS = 0;bIsValid = false;}FS_ReplayInfo1(FString NewName, FString NewFriendlyName, FDateTime NewTimestamp, int32 NewLengthInMS){ReplayName = NewName;FriendlyName = NewFriendlyName;Timestamp = NewTimestamp;LengthInMS = NewLengthInMS;bIsValid = true;}
};
/*** */
UCLASS()
class REPLAYTEST_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_ReplayInfo1>& AllReplaysm);
};
  • MyGameInstance.cpp中:

#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_ReplayInfo1> AllReplays;for (FNetworkReplayStreamInfo StreamInfo : Result.FoundStreams){void BP_OnFindReplaysComplete1(const TArray<FS_ReplayInfo1> &AllReplaysm);if (!StreamInfo.bIsLive){AllReplays.Add(FS_ReplayInfo1(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();
}//void UMyGameInstance::BP_OnFindReplaysComplete1(TArray<FS_ReplayInfo1>& AllReplays)
//{
//}
  • 创建新的C++类PC_Spectator继承自:PlayerController

  • PC_Spectator.h中:

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "PC_ReplaySpectator.generated.h"/*** */
UCLASS()
class REPLAYTEST_API APC_ReplaySpectator : public APlayerController
{GENERATED_BODY()public:/** we must set some Pause-Behavior values in the ctor */APC_ReplaySpectator(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);
};
  • PC_Spectator.cpp中:

// Fill out your copyright notice in the Description page of Project Settings.#include "PC_ReplaySpectator.h"
#include "Engine/World.h"
#include "Engine/DemoNetDriver.h"APC_ReplaySpectator::APC_ReplaySpectator(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{bShowMouseCursor = true;PrimaryActorTick.bTickEvenWhenPaused = true;bShouldPerformFullTickWhenPaused = true;
}
bool APC_ReplaySpectator::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->Pauser = PlayerState;return true;}// Rest MotionBlur and AACVarAA->Set(PreviousAASetting);CVarMB->Set(PreviousMBSetting);WorldSettings->Pauser = NULL;return false;
}
int32 APC_ReplaySpectator::GetCurrentReplayTotalTimeInSeconds() const
{if (GetWorld()){if (GetWorld()->DemoNetDriver){return GetWorld()->DemoNetDriver->DemoTotalTime;}}return 0.f;
}int32 APC_ReplaySpectator::GetCurrentReplayCurrentTimeInSeconds() const
{if (GetWorld()){if (GetWorld()->DemoNetDriver){return GetWorld()->DemoNetDriver->DemoCurrentTime;}}return 0.f;
}void APC_ReplaySpectator::SetCurrentReplayTimeToSeconds(int32 Seconds)
{if (GetWorld()){if (GetWorld()->DemoNetDriver){GetWorld()->DemoNetDriver->GotoTimeInSeconds(Seconds);}}
}void APC_ReplaySpectator::SetCurrentReplayPlayRate(float PlayRate)
{if (GetWorld()){if (GetWorld()->DemoNetDriver){GetWorld()->GetWorldSettings()->DemoPlayTimeDilation = PlayRate;}}
}
  • 编译成功后启动引擎进入下一步;

2.编写蓝图及其他设置:

  1. 分别创建用户控件WBP_ReplayChildWBP_ReplayMenuWBP_ReplaySpectator

  2. 创建新的蓝图类GI_Replay继承自MyGameInstance并重载函数BP on Find Replays Complete 1

  3. 创建新的控制器类BP_PB_ReplaySpectator继承自PC_ReplaySpectator

  4. 打开第一人称&第三人称游戏模式蓝图,设置重播旁观者为创建的PC_ReplaySpectator

  5. 两人称角色蓝图中类默认值开启复制&复制运动,写入录制逻辑

  6. 打开所需录制的地图(一三人称地图)分别对应世界场景设置游戏模式开启所有可移动物细节栏中复制

  7. 项目设置中设置默认地图游戏实例

  8. 创建空地图MainMenuMap,游戏模式选择None关卡蓝图中添加菜单到视口:

    3.开始运行,进入录制或进行回放。


    视频教程:BV17L4y1A7eF​​​​​​​

    【虚幻4】Replay重播系统-UE4.26教程

【UE4】Replay游戏回放 for UE4.26相关推荐

  1. 《Exploring in UE4》游戏角色的移动原理(下)

    前言 上一篇主要从单机角色的移动原理进行分析.今天这篇文章会详细的分析多个玩家的移动同步是如何处理的.文章的内容可能比较深,需要读者有一定游戏开发经验,而且结合引擎源码才能更好的理解,建议收藏找时间慢 ...

  2. 《Exploring in UE4》Unreal回放系统剖析(上)

    回放,是电子游戏中一项常见的功能,用于记录整个比赛过程或者展示游戏中的精彩瞬间.通过回放,我们可以观摩高手之间的对决,享受游戏中的精彩瞬间,甚至还可以拿到敌方玩家的比赛录像进行分析和学习. 从实现技术 ...

  3. UE4三维游戏毕设制作与学习过程中的所思所想01

    提示:前面是一大堆可看可不看的"废话". 随着毕设Deadline的越来越近,在前期模型制作上遇到的问题大多都已得到解决,但是在这个过程中却一直没想过写些文字记录下来这个学习的过程 ...

  4. 游戏开发入门ue4和unity要怎么选择?

    关于两款引擎该怎么选的问题 先从行业大环境这个角度分析:目前市场主流是U3D,这并不意味着UE4不好.UE4的学习成本更 高且招人困难导致了大部分公司不选择用UE4去做游戏.U3D是做端游也可以,做手 ...

  5. 《巫师3》、UE4与游戏

    初稿:2021年01月02日 一些我最近在做的一些事. <巫师3> <巫师3>呀,这么优秀的游戏!一直以来,我特别想知道这样庞大而复杂的游戏是怎么制作的.前段时间,找了部分&l ...

  6. UE4创建游戏补丁以及DLC的方法

    UE4创建游戏补丁以及DLC的方法 https://blog.csdn.net/hl1282456555/article/details/79457599 一直以来都比较好奇UE4制作出来的游戏该怎么 ...

  7. 【UE4】二十三、UE4笔试面试题

    在CSDN博客看到的,带着这些问题,多多留意,正所谓带着问题学习. 一. 1.Actor的EndPlay事件在哪些时候会调用? 2.BlueprintImplementableEvent和Bluepr ...

  8. 从UE4源代码启动、创建UE4新项目

    (该方法在windows 10 x64专业版和UE4 4.25.1版本上得到验证) 运行环境: .net framework 4.6.2 VSC 2019 方法如下: 得到UE4源代码(下载方法见官方 ...

  9. 网易游戏平台 v1.1.26 官方最新版​

    网易游戏平台 v1.1.26 官方最新版 软件大小:43.7MB 软件语言:简体中文 软件类别:游戏大厅 软件授权:官方版 更新时间:2015-01-28 应用平台:/Win8/Win7/WinXP ...

最新文章

  1. https://www.exploit-db.com/能够成功下载的一个CVE编号
  2. 【工作秘籍】Facebook内部高效工作大揭秘
  3. linux分析文件格式,linux elf文件格式分析
  4. 《幽灵行动·荒野》中的程序化技术:道路、河流、城镇、植被
  5. linux系统 qt调试,Linux下Qt Creator远程调试(redhat5+mini6410)
  6. 八种排序整理(六)----堆排序
  7. php算法求出一个数可以被分解成多少个_最全的小学干货:34个数学重难点公式(三)...
  8. ANSI字符与Unicode字符的互相转换
  9. 还在担心机器人?人工智能目前水平还不如初中生
  10. Excel 中批量处理数据(改成 json 格式)
  11. Ubuntu18.04+Halcon18.11安装教程
  12. 首个在线教学中文预训练模型TAL-EduBERT
  13. NUC1041 数字三角形【DP】
  14. rails--bcrypt对密码加密
  15. 与原子操作相关的 volatile 和 sig_atmotic_t
  16. ThinkpadX220 windows10 博通bcm94352hmb的蓝牙连接音箱播放声音断断续续的解决方案
  17. ce修改植物大战僵尸之修改阳光值
  18. SDN架构技术报告:北向与南向协议
  19. springboot 的异步任务 :无返回值 和有返回值
  20. 专访神州飞象高级数据库工程师赖伟:迁移不怕难,大象肚里能撑船

热门文章

  1. android获取静态apk包,如何从APK文件提取API调用?
  2. 小学计算机教育实习教案,信息技术环境下小学综合实践活动课教学设计
  3. .Net界面开发必备!DevExpress Blazor UI全新组件助力界面开发
  4. pbootcms火车头免登录发布模块pbootcms入库插件
  5. 悟空说财经:雅虎推出全新聊天机器人 帮家庭成员管理日程安排
  6. Active Directory 与域
  7. 紫薇盘反推出生年月时
  8. 解决Android编译时出现的java.lang.UnsatisfiedLinkError问题!
  9. average-population-of-each-continent
  10. 毕业设计-基于Javaweb心理咨询预约管理系统