关于unreal-engine4:Exploring-in-UE4Unreal回放系统剖析下
本篇分为高低两篇,上篇内容请关注:《Exploring in UE4》Unreal回放零碎分析(上) 四、死亡回放/精彩镜头性能的实现在FPS游戏里,一个角色被击杀之后,往往会以敌方的视角回放本角色被定位、瞄准、射击的过程,这就是咱们常提到的死亡回放(DeathCameraReplay)。相似的,咱们在各种体育游戏外面常常须要在一次得分后展现精彩霎时,这种性能个别称为精彩镜头。 上一节案例应用的是基于本地文件存储的回放零碎,每次播放时都须要从新加载地图。那有没有方法实现相似实况足球的实时精彩回放呢?有的,那就是基于DuplicatedLevelCollection和内存数据流的回放计划。 思考一下,通常射击游戏里的击杀镜头、体育竞技里的精彩时刻对回放的根本需要是什么?这类回放性能往往是在某个工夫点能够无感知地立即切换到回放镜头,并在回放完结后迅速再切换到失常的游戏环境。同时,思考到联机的状况,咱们在回放时要放弃游戏世界的失常运行,从而确保不错过任何服务器的同步信息,不影响其余玩家。 简略总结就是: 能够迅速地在实在游戏与回放镜头间切换回放的时候不会影响实在游戏外面的逻辑变动4.1 回放场景与实在场景拆散为了实现上述的要求,咱们须要将回放的场景和实在的场景进行拆散,在不从新加载地图的状况下疾速地进行切换。空幻引擎给出的计划是对游戏世界World进行进一步的拆分,把所有的Level组织到了三个LevelCollection外面,别离是: DynamicSourceLevels,存储真实世界的所有标记为Dynamic的Level(蕴含外面的所有Actor)StaticLevels,存储了动态的Actor,也就是回放过程中不会发生变化的对象,通常指那些不可毁坏修建(通过关卡编辑器外面的Static选项,能够设置任何一个SubLevel是属于DynamicSourceLevels还是StaticLevels的,PersistLevel永远是Dynamic的)DynamicDuplicatedLevels,回放世界的Level(蕴含外面的所有Actor),会把DynamicSourceLevels外面的所有Level都复制一遍 在游戏地图Loading的时候,咱们就会把这三种LevelCollection全副构建并加载进来(能够通过Experimental_ShouldPreDuplicateMap来决定某张地图是否能够复制Level到DynamicDuplicatedLevels),这样在进行回放的时候咱们只有管制LevelCollection的显示和暗藏就能够霎时对真实世界和回放世界进行切换了。 判断一个对象是否处于回放世界(DynamicDuplicatedLevels)也很简略。 UWorld* World = WorldContextObject->GetWorld();ULevel* Level = Cast<ULevel>(WorldContextObject->GetTypedOuter<ULevel>());if (World && Level){ FLevelCollection* const DuplicateCollection = World->FindCollectionByType(ELevelCollectionType::DynamicDuplicatedLevels); if (DuplicateCollection) { for (auto& TempLevel : DuplicateCollection->GetLevels()) { if (TempLevel == Level) { return true; } } }}要留神的是,因为LevelCollection的引入,原来很多逻辑都变得复杂了。 不同LevelCollection的Tick是有先后顺序的,默认状况下是依照他们在数组的排列程序DynamicSourceLevels-> StaticLevels-> DynamicDuplicatedLevels,这个程序可能影响咱们的代码逻辑或者摄像机更新机会。回放世界DynamicDuplicatedLevels外面也会有很多Actor,如果不加解决的话很有可能也被录制到回放零碎中,造成嵌套录制。当一个DynamicDuplicatedLevels执行Tick的时候,会通过FScopedLevelCollectionContextSwitch来切换以后的ActiveCollection,进而批改以后World的GameState等指针,所以在回放时须要留神获取对象的正确性。(比方下图获取PC的迭代器接口,在DuplicatedLevels Tick时只能获取到回放世界的PC)。用于回放的UDemoNetDriver会绑定一个LevelCollection(通过传入PlayReplay的参数LevelPrefixOverride来决定)。当触发回放逻辑后,即UDemoNetDriver::TickDispatch每帧解析回放数据时,咱们也会通过FScopedLevelCollectionContextSwitch被动切换到以后DemoNetDriver绑定的LevelCollection,保障解析回放数据时能够通过Outer找到回放场景(DynamicDuplicatedLevels) ////3/////FScopedLevelCollectionContextSwitch::FScopedLevelCollectionContextSwitch(const FLevelCollection* const InLevelCollection, UWorld* const InWorld){ if (World) { const int32 FoundIndex = World->GetLevelCollections().IndexOfByPredicate([InLevelCollection](const FLevelCollection& Collection) { return &Collection == InLevelCollection; }); World->SetActiveLevelCollection(FoundIndex); }}void UWorld::SetActiveLevelCollection(int32 LevelCollectionIndex){ ActiveLevelCollectionIndex = LevelCollectionIndex; const FLevelCollection* const ActiveLevelCollection = GetActiveLevelCollection(); if (ActiveLevelCollection == nullptr) { return; } PersistentLevel = ActiveLevelCollection->GetPersistentLevel(); GameState = ActiveLevelCollection->GetGameState(); NetDriver = ActiveLevelCollection->GetNetDriver(); DemoNetDriver = ActiveLevelCollection->GetDemoNetDriver(); }////4////bool UDemoNetDriver::InitConnect(FNetworkNotify* InNotify, const FURL& ConnectURL, FString& Error){ const TCHAR* const LevelPrefixOverrideOption = ConnectURL.GetOption(TEXT("LevelPrefixOverride="), nullptr); if (LevelPrefixOverrideOption) { SetDuplicateLevelID(FCString::Atoi(LevelPrefixOverrideOption)); } if (GetDuplicateLevelID() == -1) { // Set this driver as the demo net driver for the source level collection. FLevelCollection* const SourceCollection = World->FindCollectionByType(ELevelCollectionType::DynamicSourceLevels); if (SourceCollection) { SourceCollection->SetDemoNetDriver(this); } } else { // Set this driver as the demo net driver for the duplicate level collection. FLevelCollection* const DuplicateCollection = World->FindCollectionByType(ELevelCollectionType::DynamicDuplicatedLevels); if (DuplicateCollection) { DuplicateCollection->SetDemoNetDriver(this); } } }4.2 回放录制与播放拆散思考到在死亡回放的时候不会影响失常较量的进行和录制,所以咱们通常也须要讲录制逻辑与播放逻辑齐全拆散。 ...