UE4之LoadingScreen机制
1. 当IsMoviePlayerEnabled()为false或使用nullrhi来运行游戏时,会使用FNullGameMoviePlayer(空MoviePlayer)来跑
2. FDefaultGameMoviePlayer为实现LoadingScreen的核心类,包括视频数据流(IMovieStreamer)、UI界面(SWindow)、多线程更新(SlateLoadingThread)
3. 各个平台(Android、iOS、Windows)实现了自己的IMovieStreamer,用于解码视频文件,一个IMovieStreamer对应一个视频文件。
各平台上的IMovieStreamer对象伴随FXXXMoviePlayerModule平台模块的加载来创建和注册到FDefaultGameMoviePlayer对象中,卸载来反注册和删除
TSharedPtr<FXXXMediaPlayerStreamer> XXXMovieStreamer; // 全局智能指针对象 class FXXXMoviePlayerModule : public IModuleInterface { /** IModuleInterface implementation */ virtual void StartupModule() override { FXXXMediaPlayerStreamer* Streamer = new FXXXMediaPlayerStreamer; XXXMovieStreamer = MakeShareable(Streamer); FCoreDelegates::RegisterMovieStreamerDelegate.Broadcast(XXXMovieStreamer); } virtual void ShutdownModule() override { if (XXXMovieStreamer.IsValid()) { FCoreDelegates::UnRegisterMovieStreamerDelegate.Broadcast(XXXMovieStreamer); } XXXMovieStreamer->Cleanup(); XXXMovieStreamer.Reset(); } };
一些MoviePlayer相关的函数:
/*** UnrealEngine\Engine\Source\Runtime\MoviePlayer\Public\MoviePlayer.h ***/ /** Creates the movie player */ MOVIEPLAYER_API void CreateMoviePlayer(); /** Destroys the movie player */ MOVIEPLAYER_API void DestroyMoviePlayer(); /** Gets the movie player singleton for the engine. */ MOVIEPLAYER_API IGameMoviePlayer* GetMoviePlayer(); MOVIEPLAYER_API IGameMoviePlayer& GetMoviePlayerRef(); /** Returns true if the movie player is enabled. */ bool MOVIEPLAYER_API IsMoviePlayerEnabled();
FSlateLoadingSynchronizationMechanism::SlateThreadRunMainLoop()
/** UnrealEngine\Engine\Source\Runtime\MoviePlayer\Private\MoviePlayerThreading.cpp **/ void FSlateLoadingSynchronizationMechanism::SlateThreadRunMainLoop() { ThreadSuspendedEvent = FPlatformProcess::GetSynchEventFromPool(); ThreadResumedEvent = FPlatformProcess::GetSynchEventFromPool(); double LastTime = FPlatformTime::Seconds(); bool bWasSuspendedLastFrame = false; while (IsSlateMainLoopRunning()) { if (IsSuspended.GetValue() == 0) { if (bWasSuspendedLastFrame) { bWasSuspendedLastFrame = false; ThreadResumedEvent->Trigger(); } { double CurrentTime = FPlatformTime::Seconds(); double DeltaTime = CurrentTime - LastTime; // 60 fps max const double MaxTickRate = 1.0 / 60.0f; const double TimeToWait = MaxTickRate - DeltaTime; if (TimeToWait > 0) { FPlatformProcess::Sleep(TimeToWait); CurrentTime = FPlatformTime::Seconds(); DeltaTime = CurrentTime - LastTime; } if (FSlateApplication::IsInitialized() && !IsSlateDrawPassEnqueued()) { FSlateRenderer* MainSlateRenderer = FSlateApplication::Get().GetRenderer(); FScopeLock ScopeLock(MainSlateRenderer->GetResourceCriticalSection()); WidgetRenderer->DrawWindow(DeltaTime); SetSlateDrawPassEnqueued(); } LastTime = CurrentTime; } } else if (!bWasSuspendedLastFrame) { bWasSuspendedLastFrame = true; ThreadSuspendedEvent->Trigger(); } else { FPlatformProcess::SleepNoStats(0.001f); } } while (IsSlateDrawPassEnqueued()) { FPlatformProcess::Sleep(1.f / 60.f); } bMainLoopRunning = false; FPlatformProcess::ReturnSynchEventToPool(ThreadSuspendedEvent); ThreadSuspendedEvent = nullptr; FPlatformProcess::ReturnSynchEventToPool(ThreadResumedEvent); ThreadResumedEvent = nullptr; }
FMoviePlayerWidgetRenderer::DrawWindow()
/** UnrealEngine\Engine\Source\Runtime\MoviePlayer\Private\DefaultGameMoviePlayer.cpp **/ void FMoviePlayerWidgetRenderer::DrawWindow(float DeltaTime) { if (GDynamicRHI && GDynamicRHI->RHIIsRenderingSuspended()) { // This avoids crashes if we Suspend rendering whilst the loading screen is up // as we don't want Slate to submit any more draw calls until we Resume. return; } const float Scale = FSlateApplication::Get().GetApplicationScale() * MainWindow->GetDPIScaleFactor(); FVector2D DrawSize = VirtualRenderWindow->GetClientSizeInScreen() / Scale; FSlateApplication::Get().Tick(ESlateTickType::Time); FGeometry WindowGeometry = FGeometry::MakeRoot(DrawSize, FSlateLayoutTransform(Scale)); VirtualRenderWindow->SlatePrepass(WindowGeometry.Scale); FSlateRect ClipRect = WindowGeometry.GetLayoutBoundingRect(); HittestGrid->SetHittestArea(VirtualRenderWindow->GetPositionInScreen(), VirtualRenderWindow->GetViewportSize()); HittestGrid->Clear(); // Get the free buffer & add our virtual window FSlateDrawBuffer& DrawBuffer = SlateRenderer->GetDrawBuffer(); FSlateWindowElementList& WindowElementList = DrawBuffer.AddWindowElementList(VirtualRenderWindow); WindowElementList.SetRenderTargetWindow(MainWindow); int32 MaxLayerId = 0; { FPaintArgs PaintArgs(nullptr, *HittestGrid, FVector2D::ZeroVector, FSlateApplication::Get().GetCurrentTime(), FSlateApplication::Get().GetDeltaTime()); // Paint the window MaxLayerId = VirtualRenderWindow->Paint( // TSharedRef<class SVirtualWindow> VirtualRenderWindow 注:SVirtualWindow : public SWindow PaintArgs, WindowGeometry, ClipRect, WindowElementList, 0, FWidgetStyle(), VirtualRenderWindow->IsEnabled()); } SlateRenderer->DrawWindows(DrawBuffer); // 不LoadingScreen时,该逻辑跑在GameThread上 DrawBuffer.ViewOffset = FVector2D::ZeroVector; }
正常情况下,UI界面的更新是在GameThread中处理的
> UE4Editor-SlateRHIRenderer.dll!FSlateRHIRenderer::DrawWindows_Private(FSlateDrawBuffer & WindowDrawBuffer={...}) Line 1501 C++ UE4Editor-Slate.dll!FSlateApplication::PrivateDrawWindows(TSharedPtr<SWindow,0> DrawOnlyThisWindow={...}) Line 1389 C++ UE4Editor-Slate.dll!FSlateApplication::DrawWindows() Line 1088 C++ UE4Editor-Slate.dll!FSlateApplication::TickAndDrawWidgets(float DeltaTime) Line 1663 C++ UE4Editor-Slate.dll!FSlateApplication::Tick(ESlateTickType TickType) Line 1517 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 5051 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 63 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x00000105e0b897c0) Line 174 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
当GameThread长时间忙于其他事情时,为了防止UI界面卡死,可以将UI界面的更新切换到LoadingScreen线程来跑
> UE4Editor-SlateRHIRenderer.dll!FSlateRHIRenderer::DrawWindows_Private(FSlateDrawBuffer & WindowDrawBuffer={...}) Line 1699 C++ UE4Editor-MoviePlayer.dll!FMoviePlayerWidgetRenderer::DrawWindow(float DeltaTime=-1.78076227e-33) Line 1018 C++ UE4Editor-MoviePlayer.dll!FSlateLoadingSynchronizationMechanism::SlateThreadRunMainLoop() Line 180 C++ UE4Editor-MoviePlayer.dll!FSlateLoadingThreadTask::Run() Line 258 C++ UE4Editor-Core.dll!FRunnableThreadWin::Run() Line 90 C++ UE4Editor-Core.dll!FRunnableThreadWin::GuardedRun() Line 27 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
渲染会切换到FDefaultGameMoviePlayer::Tick中来跑 注:FDefaultGameMoviePlayer从FTickableObjectRenderThread上派生
> UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::Tick(float DeltaTime) Line 658 C++ UE4Editor-RenderCore.dll!TickHighFrequencyTickables(double CurTime) Line 253 C++ UE4Editor-RenderCore.dll!TickRenderingTickables() Line 271 C++ [Inline Frame] UE4Editor-RenderCore.dll!FRenderingThreadTickHeartbeat::Run::__l7::<lambda_bfd1ddcd77fe5e349cdd0178d16972d4>::operator()(FRHICommandList &) Line 605 C++ UE4Editor-RenderCore.dll!TEnqueueUniqueRenderCommandType<`FRenderingThreadTickHeartbeat::Run'::`7'::HeartbeatTickTickablesName,<lambda_bfd1ddcd77fe5e349cdd0178d16972d4>>::DoTask(ENamedThreads::Type CurrentThread=-1601341044, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent={...}) Line 190 C++ UE4Editor-RenderCore.dll!TGraphTask<TEnqueueUniqueRenderCommandType<`FRenderingThreadTickHeartbeat::Run'::`7'::HeartbeatTickTickablesName,<lambda_bfd1ddcd77fe5e349cdd0178d16972d4>>>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread=ActualRenderingThread) Line 886 C++ [Inline Frame] UE4Editor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread=ActualRenderingThread, ENamedThreads::Type) Line 524 C++ UE4Editor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall=true) Line 709 C++ UE4Editor-Core.dll!FNamedTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 601 C++ UE4Editor-RenderCore.dll!RenderingThreadMain(FEvent * TaskGraphBoundSyncEvent=0x000001f4e4c6da80) Line 378 C++ UE4Editor-RenderCore.dll!FRenderingThread::Run() Line 537 C++ UE4Editor-Core.dll!FRunnableThreadWin::Run() Line 90 C++ UE4Editor-Core.dll!FRunnableThreadWin::GuardedRun() Line 27 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
游戏启动引擎初始化时
ntdll.dll!NtCreateSection() Unknown ntdll.dll!LdrpMapDllNtFileName() Unknown ntdll.dll!LdrpMapDllFullPath() Unknown ntdll.dll!LdrpProcessWork() Unknown ntdll.dll!LdrpLoadDllInternal() Unknown ntdll.dll!LdrpLoadDll() Unknown ntdll.dll!LdrLoadDll() Unknown KernelBase.dll!LoadLibraryExW() Unknown > UE4Editor-Core.dll!FWindowsPlatformProcess::LoadLibraryWithSearchPaths(const FString & FileName={...}, const TArray<FString,TSizedDefaultAllocator<32>> & SearchPaths={...}) Line 1889 C++ UE4Editor-Core.dll!FWindowsPlatformProcess::GetDllHandle(const wchar_t * FileName=0x000001808bb62980) Line 91 C++ UE4Editor-Core.dll!FModuleManager::LoadModuleWithFailureReason(const FName InModuleName={...}, EModuleLoadResult & OutFailureReason=Success) Line 506 C++ UE4Editor-Projects.dll!FModuleDescriptor::LoadModulesForPhase(ELoadingPhase::Type LoadingPhase=PreDefault, const TArray<FModuleDescriptor,TSizedDefaultAllocator<32>> & Modules={...}, TMap<FName,enum EModuleLoadResult,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<FName,enum EModuleLoadResult,0>> & ModuleLoadErrors={...}) Line 561 C++ UE4Editor-Projects.dll!FPluginManager::TryLoadModulesForPlugin(const FPlugin & Plugin={...}, const ELoadingPhase::Type LoadingPhase) Line 1321 C++ UE4Editor-Projects.dll!FPluginManager::LoadModulesForEnabledPlugins(const ELoadingPhase::Type LoadingPhase=PreDefault) Line 1395 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::LoadStartupModules() Line 3866 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::PreInitPostStartupScreen(const wchar_t * CmdLine=0x00007ff7e95d2b28) Line 3245 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!FEngineLoop::PreInit(const wchar_t *) Line 3649 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EnginePreInit(const wchar_t *) Line 43 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x00000180d04997c0) Line 128 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
在FEngineLoop::PreInitPostStartupScreen()函数中主动调用GetMoviePlayer()->PlayMovie()来启动LoadingScreen线程
UE4Editor-Core.dll!FRunnableThread::Create(FRunnable * InRunnable=0x0000026db4397f00, const wchar_t * ThreadName=0x0000026db441f160, unsigned int InStackSize=0, EThreadPriority InThreadPri=TPri_Normal, unsigned __int64 InThreadAffinityMask=18446744073709551615, EThreadCreateFlags InCreateFlags=None) Line 542 C++ > [Inline Frame] UE4Editor-MoviePlayer.dll!FSlateLoadingSynchronizationMechanism::Initialize() Line 63 C++ UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::PlayMovie() Line 386 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::PreInitPostStartupScreen(const wchar_t * CmdLine=0x00007ff7e95d2b28) Line 3212 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!FEngineLoop::PreInit(const wchar_t *) Line 3649 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EnginePreInit(const wchar_t *) Line 43 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000026df8c597c0) Line 128 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
在后续FEngineLoop::Init()函数中调用GetMoviePlayer()->WaitForMovieToFinish()来结束LoadingScreen线程
> UE4Editor-MoviePlayer.dll!FSlateLoadingSynchronizationMechanism::DestroySlateThread() Line 81 C++ UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::WaitForMovieToFinish(bool bAllowEngineTick=false) Line 443 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Init() Line 4061 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineInit() Line 53 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000026df8c597c0) Line 156 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
LoadMap设置了LoadingScreen时
FDefaultGameMoviePlayer::Initialize()时,会注册PreLoadMap的代理,使得在加载地图之前开始执行PlayMovie()来启动LoadingScreen线程
UE4Editor-Core.dll!FRunnableThread::Create(FRunnable * InRunnable=0x0000019c2e75edf0, const wchar_t * ThreadName=0x0000019c2e9f4ee0, unsigned int InStackSize=0, EThreadPriority InThreadPri=TPri_Normal, unsigned __int64 InThreadAffinityMask=18446744073709551615, EThreadCreateFlags InCreateFlags=None) Line 551 C++ > [Inline Frame] UE4Editor-MoviePlayer.dll!FSlateLoadingSynchronizationMechanism::Initialize() Line 63 C++ UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::PlayMovie() Line 386 C++ UE4Editor-StreamingPauseRendering.dll!FStreamingPauseRenderingModule::BeginStreamingPause(FViewport * GameViewport=0x0000019c305c4460) Line 160 C++ [Inline Frame] UE4Editor-Engine.dll!TDelegate<void __cdecl(FViewport *),FDefaultDelegateUserPolicy>::Execute(FViewport * <Params_0>) Line 580 C++ UE4Editor-Engine.dll!UEngine::BlockTillLevelStreamingCompleted(UWorld * InWorld=0x0000019c32148b80) Line 13969 C++ UE4Editor-Engine.dll!AGameMode::HandleMatchHasStarted() Line 242 C++ UE4Editor-Engine.dll!AGameMode::SetMatchState(FName NewState={...}) Line 368 C++ UE4Editor-Engine.dll!AGameMode::StartMatch() Line 216 C++ UE4Editor-Engine.dll!UWorld::BeginPlay() Line 4616 C++ UE4Editor-Engine.dll!UEngine::LoadMap(FWorldContext & WorldContext={...}, FURL URL={...}, UPendingNetGame * Pending=0xffffffffffffffff, FString & Error={...}) Line 13880 C++ UE4Editor-Engine.dll!UEngine::Browse(FWorldContext & WorldContext={...}, FURL URL={...}, FString & Error={...}) Line 12928 C++ UE4Editor-Engine.dll!UEngine::TickWorldTravel(FWorldContext & Context={...}, float DeltaSeconds) Line 13126 C++ UE4Editor-Engine.dll!UGameEngine::Tick(float DeltaSeconds=11.7816048, bool bIdleMode=false) Line 1863 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 4939 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 63 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000019bc60d97c0) Line 174 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
在PostLoadMap时执行WaitForMovieToFinish()来结束LoadingScreen线程
> UE4Editor-MoviePlayer.dll!FSlateLoadingSynchronizationMechanism::DestroySlateThread() Line 81 C++ UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::WaitForMovieToFinish(bool bAllowEngineTick=false) Line 443 C++ [Inline Frame] UE4Editor-MoviePlayer.dll!Invoke(void(FDefaultGameMoviePlayer::*)(UWorld *)) Line 65 C++ [Inline Frame] UE4Editor-MoviePlayer.dll!UE4Tuple_Private::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(FDefaultGameMoviePlayer::*)(UWorld *) &) Line 299 C++ UE4Editor-MoviePlayer.dll!TBaseRawMethodDelegateInstance<0,FDefaultGameMoviePlayer,void __cdecl(UWorld *),FDefaultDelegateUserPolicy>::ExecuteIfSafe(UWorld * <Params_0>) Line 469 C++ UE4Editor-Engine.dll!TMulticastDelegate<void __cdecl(UWorld *),FDefaultDelegateUserPolicy>::Broadcast(UWorld * <Params_0>=0x0000019c1d957580) Line 955 C++ UE4Editor-Engine.dll!UEngine::LoadMap(FWorldContext & WorldContext={...}, FURL URL={...}, UPendingNetGame * Pending=0xffffffffffffffff, FString & Error={...}) Line 13885 C++ UE4Editor-Engine.dll!UEngine::Browse(FWorldContext & WorldContext={...}, FURL URL={...}, FString & Error={...}) Line 12928 C++ UE4Editor-Engine.dll!UEngine::TickWorldTravel(FWorldContext & Context={...}, float DeltaSeconds) Line 13126 C++ UE4Editor-Engine.dll!UGameEngine::Tick(float DeltaSeconds=0.0333334021, bool bIdleMode=false) Line 1863 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 4939 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 63 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000019bc60d97c0) Line 174 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
FDefaultGameMoviePlayer中OnPreLoadMap和OnPostLoadMap函数
/** UnrealEngine\Engine\Source\Runtime\MoviePlayer\Private\DefaultGameMoviePlayer.cpp **/ void FDefaultGameMoviePlayer::OnPreLoadMap(const FString& LevelName) { FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this); if( PlayMovie() ) { FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FDefaultGameMoviePlayer::OnPostLoadMap ); } } void FDefaultGameMoviePlayer::OnPostLoadMap(UWorld* LoadedWorld) { if (!LoadingScreenAttributes.bAllowEngineTick) { // If engine tick is enabled, we don't want to tick here and instead want to run from the WaitForMovieToFinish call in LaunchEngineLoop WaitForMovieToFinish(); } }
LoadMap没有设置LoadingScreen时
LoadMap时FStreamingPauseRenderingModule会执行PlayMovie()和WaitForMovieToFinish()
右下角会有三个点的UI动画表现(throbber),详见:UnrealEngine\Engine\Source\Runtime\Slate\Private\Widgets\Images\SThrobber.cpp
// FDefaultGameMoviePlayer::PlayMovie() > UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::PlayMovie() Line 385 C++ UE4Editor-StreamingPauseRendering.dll!FStreamingPauseRenderingModule::BeginStreamingPause(FViewport * GameViewport=0x0000026e34448d80) Line 160 C++ [Inline Frame] UE4Editor-Engine.dll!TDelegate<void __cdecl(FViewport *),FDefaultDelegateUserPolicy>::Execute(FViewport * <Params_0>) Line 580 C++ UE4Editor-Engine.dll!UEngine::BlockTillLevelStreamingCompleted(UWorld * InWorld=0x0000026e23952080) Line 13969 C++ UE4Editor-Engine.dll!AGameMode::HandleMatchHasStarted() Line 242 C++ UE4Editor-Engine.dll!AGameMode::SetMatchState(FName NewState={...}) Line 368 C++ UE4Editor-Engine.dll!AGameMode::StartMatch() Line 216 C++ UE4Editor-Engine.dll!UWorld::BeginPlay() Line 4616 C++ UE4Editor-Engine.dll!UEngine::LoadMap(FWorldContext & WorldContext={...}, FURL URL={...}, UPendingNetGame * Pending=0xffffffffffffffff, FString & Error={...}) Line 13880 C++ UE4Editor-Engine.dll!UEngine::Browse(FWorldContext & WorldContext={...}, FURL URL={...}, FString & Error={...}) Line 12928 C++ UE4Editor-Engine.dll!UEngine::TickWorldTravel(FWorldContext & Context={...}, float DeltaSeconds) Line 13126 C++ UE4Editor-Engine.dll!UGameEngine::Tick(float DeltaSeconds=104.692589, bool bIdleMode=false) Line 1863 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 4939 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 63 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000026df8c597c0) Line 174 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown // FDefaultGameMoviePlayer::WaitForMovieToFinish() > UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::WaitForMovieToFinish(bool bAllowEngineTick=false) Line 436 C++ UE4Editor-StreamingPauseRendering.dll!FStreamingPauseRenderingModule::EndStreamingPause() Line 173 C++ [Inline Frame] UE4Editor-Engine.dll!TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy>::Execute() Line 580 C++ UE4Editor-Engine.dll!UEngine::BlockTillLevelStreamingCompleted(UWorld * InWorld=0x0000026e23952080) Line 13973 C++ UE4Editor-Engine.dll!AGameMode::HandleMatchHasStarted() Line 242 C++ UE4Editor-Engine.dll!AGameMode::SetMatchState(FName NewState={...}) Line 368 C++ UE4Editor-Engine.dll!AGameMode::StartMatch() Line 216 C++ UE4Editor-Engine.dll!UWorld::BeginPlay() Line 4616 C++ UE4Editor-Engine.dll!UEngine::LoadMap(FWorldContext & WorldContext={...}, FURL URL={...}, UPendingNetGame * Pending=0xffffffffffffffff, FString & Error={...}) Line 13880 C++ UE4Editor-Engine.dll!UEngine::Browse(FWorldContext & WorldContext={...}, FURL URL={...}, FString & Error={...}) Line 12928 C++ UE4Editor-Engine.dll!UEngine::TickWorldTravel(FWorldContext & Context={...}, float DeltaSeconds) Line 13126 C++ UE4Editor-Engine.dll!UGameEngine::Tick(float DeltaSeconds=104.692589, bool bIdleMode=false) Line 1863 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 4939 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 63 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000026df8c597c0) Line 174 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
主循环Tick中调用FDefaultGameMoviePlayer::WaitForMovieToFinish
> UE4Editor-MoviePlayer.dll!FDefaultGameMoviePlayer::WaitForMovieToFinish(bool bAllowEngineTick=true) Line 436 C++ UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 4963 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 63 C++ UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine=0x0000026df8c597c0) Line 174 C++ UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance=0x000000000000000a, HINSTANCE__ * hPrevInstance=0x0000000000000000, char * __formal=0x0000000000000000, int nCmdShow=0) Line 275 C++ [Inline Frame] UE4Editor-Win64-DebugGame.exe!invoke_main() Line 102 C++ UE4Editor-Win64-DebugGame.exe!__scrt_common_main_seh() Line 288 C++ kernel32.dll!BaseThreadInitThunk() Unknown ntdll.dll!RtlUserThreadStart() Unknown
扩展阅读
Async Loading Screens and Transition Levels | Unreal Fest Europe 2019 | Unreal Engine