本文参考:
https://github.com/ue4plugins/LoadingScreen
https://wiki.unrealengine.com/Loading_Screen
因为openlevel是阻塞式加载场景, 为了在切地图的时候,有loading图, 所以需要异步线程显示loading图.
LoadingScreen 就是做这个工作的.
使用loading screen时, 必须注意以下几点:
-
必须在launch game模式下运行.
image.png
image.png -
必须关闭Pre-Loading Screen Movie Player插件
image.png -
FLoadingScreenAttributes 中Movies必须有内容.(在window下是任何字符串都行, 即使没有对应的资源也可以. 在andorid和Apple下必须要有相应的资源. )如果没有, loading就不会显示
image.png
在使用MoviePlayer的时候, 通过接口获得:
IGameMoviePlayer* GetMoviePlayer()
{
if (!IsMoviePlayerEnabled() || GUsingNullRHI)
{
return FNullGameMoviePlayer::Get();
}
else
{
return FDefaultGameMoviePlayer::Get();
}
}
这里关键的函数IsMoviePlayerEnabled, 即在编辑器模式是永远不能启动FDefaultGameMoviePlayer类的.
而FNullGameMoviePlayer类是什么都不做的. 这个就解释了为什么loading screen必须在非editor模式下运行.
bool IsMoviePlayerEnabled()
{
bool bEnabled = !GIsEditor && !IsRunningDedicatedServer() && !IsRunningCommandlet() && GUseThreadedRendering;
#if !UE_BUILD_SHIPPING
bEnabled &= !FParse::Param(FCommandLine::Get(), TEXT("NoLoadingScreen"));
#endif
return bEnabled;
}
在启动loadingscreen时候, 启动方法:
GetMoviePlayer()->SetupLoadingScreen(...);
手动停止LoadingScreen方法:
GetMoviePlayer()->StopMovie()
在游戏启动(或者editor启动)的时候, 就确定了是哪个类进行创建.
void CreateMoviePlayer()
{
// Do not create the movie player if it already exists
if(!GetMoviePlayer())
{
if (!IsMoviePlayerEnabled() || GUsingNullRHI)
{
return FNullGameMoviePlayer::Create();
}
else
{
return FDefaultGameMoviePlayer::Create();
}
}
}
image.png
在FDefaultGameMoviePlayer init的时候, 注册了一个核心回调函数:PreLoadMap
该函数就是在loadmap之前回调处理各种事件用了, 其实如果想实现一个自己的loadingscreen, 就可以创建一个线程, 利用该回调, 进行自身的处理.
void FDefaultGameMoviePlayer::Initialize(FSlateRenderer& InSlateRenderer, TSharedPtr<SWindow> TargetRenderWindow)
{
... 179行
// Add a delegate to start playing movies when we start loading a map
FCoreUObjectDelegates::PreLoadMap.AddRaw( this, &FDefaultGameMoviePlayer::OnPreLoadMap );
}
当游戏运行的时候:
image.png
这个函数就是在启动的时候播放一个启动动画.
void FDefaultGameMoviePlayer::SetupLoadingScreenFromIni()
{
// We may have already setup a movie from a startup module
if( !LoadingScreenAttributes.IsValid() )
{
// fill out the attributes
FLoadingScreenAttributes LoadingScreen;
bool bWaitForMoviesToComplete = false;
// Note: this code is executed too early so we cannot access UMoviePlayerSettings because the configs for that object have not been loaded and coalesced . Have to read directly from the configs instead
GConfig->GetBool(TEXT("/Script/MoviePlayer.MoviePlayerSettings"), TEXT("bWaitForMoviesToComplete"), bWaitForMoviesToComplete, GGameIni);
GConfig->GetBool(TEXT("/Script/MoviePlayer.MoviePlayerSettings"), TEXT("bMoviesAreSkippable"), LoadingScreen.bMoviesAreSkippable, GGameIni);
LoadingScreen.bAutoCompleteWhenLoadingCompletes = !bWaitForMoviesToComplete;
TArray<FString> StartupMovies;
GConfig->GetArray(TEXT("/Script/MoviePlayer.MoviePlayerSettings"), TEXT("StartupMovies"), StartupMovies, GGameIni);
if (StartupMovies.Num() == 0)
{
StartupMovies.Add(TEXT("Default_Startup"));
}
// double check that the movies exist
// We dont know the extension so compare against any file in the directory with the same name for now
// @todo New Movie Player: movies should have the extension on them when set via the project settings
TArray<FString> ExistingMovieFiles;
IFileManager::Get().FindFiles(ExistingMovieFiles, *(FPaths::ProjectContentDir() + TEXT("Movies")));
bool bHasValidMovie = false;
for(const FString& Movie : StartupMovies)
{
bool bFound = ExistingMovieFiles.ContainsByPredicate(
[&Movie](const FString& ExistingMovie)
{
return ExistingMovie.Contains(Movie);
});
if(bFound)
{
bHasValidMovie = true;
LoadingScreen.MoviePaths.Add(Movie);
}
}
if(bHasValidMovie)
{
// These movies are all considered safe to play in very early startup sequences
LoadingScreen.bAllowInEarlyStartup = true;
// now setup the actual loading screen
SetupLoadingScreen(LoadingScreen);
}
}
}
真正播放play Movie的函数:
void FDefaultGameMoviePlayer::OnPreLoadMap(const FString& LevelName)
{
FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this);
if( PlayMovie() )
{
FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FDefaultGameMoviePlayer::OnPostLoadMap );
}
}
在执行PlayMovie时候, 会根据LoadingScreenAttributes.MoviePaths初始化ActiveMovieStreamer, 如果ActiveMovieStreamer是无效, 则启动失败,这就要求一定要设置MoviePaths.
bool FDefaultGameMoviePlayer::PlayMovie()
{
bool bBeganPlaying = false;
// Allow systems to hook onto the movie player and provide loading screen data on demand
// if it has not been setup explicitly by the user.
if ( !LoadingScreenIsPrepared() )
{
OnPrepareLoadingScreenDelegate.Broadcast();
}
if (LoadingScreenIsPrepared() && !IsMovieCurrentlyPlaying() && FPlatformMisc::NumberOfCores() > 1)
{
check(LoadingScreenAttributes.IsValid());
bUserCalledFinish = false;
LastPlayTime = FPlatformTime::Seconds();
ActiveMovieStreamer.Reset();
if (MovieStreamingIsPrepared())
{
for (TSharedPtr<IMovieStreamer> MovieStreamer : MovieStreamers)
{
if (MovieStreamer->Init(LoadingScreenAttributes.MoviePaths, LoadingScreenAttributes.PlaybackType))
{
ActiveMovieStreamer = MovieStreamer;
if (MovieViewportWeakPtr.IsValid())
{
MovieViewportWeakPtr.Pin()->SetViewportInterface(MovieStreamer->GetViewportInterface().ToSharedRef());
}
break;
}
}
}
if (ActiveMovieStreamer.IsValid())
{
MovieStreamingIsDone.Set(MovieStreamingIsPrepared() ? 0 : 1);
LoadingIsDone.Set(0);
IsMoviePlaying = true;
UserWidgetDPIScaler->SetDPIScale(GetViewportDPIScale());
UserWidgetHolder->SetContent(LoadingScreenAttributes.WidgetLoadingScreen.IsValid() ? LoadingScreenAttributes.WidgetLoadingScreen.ToSharedRef() : SNullWidget::NullWidget);
VirtualRenderWindow->Resize(MainWindow.Pin()->GetClientSizeInScreen());
VirtualRenderWindow->SetContent(LoadingScreenContents.ToSharedRef());
{
FScopeLock SyncMechanismLock(&SyncMechanismCriticalSection);
SyncMechanism = new FSlateLoadingSynchronizationMechanism(WidgetRenderer);
SyncMechanism->Initialize();
}
bBeganPlaying = true;
}
//Allow anything that set up this LoadingScreenAttribute to know the loading screen is now displaying
if (bBeganPlaying)
{
OnMoviePlaybackStarted().Broadcast();
}
}
return bBeganPlaying;
}
在window下MovieStreamer->Init执行的是FMediaFoundationMovieStreamer::Init, 所以, 可以随点填
bool FMediaFoundationMovieStreamer::Init(const TArray<FString>& MoviePaths, TEnumAsByte<EMoviePlaybackType> inPlaybackType)
{
if (MoviePaths.Num() == 0)
{
return false;
}
MovieIndex = 0;
PlaybackType = inPlaybackType;
StoredMoviePaths = MoviePaths;
MovieViewport->SetTexture(nullptr);
OpenNextMovie();
return true;
}
在android下调用FAndroidMediaPlayerStreamer::Init, 所以必须有对应的资源文件
bool FAndroidMediaPlayerStreamer::Init(const TArray<FString>& MoviePaths, TEnumAsByte<EMoviePlaybackType> inPlaybackType)
{
{
FScopeLock Lock(&MovieQueueCriticalSection);
MovieQueue.Append(MoviePaths);
}
return StartNextMovie();
}
在Apple下也必须有对应的资源文件:
bool FAVPlayerMovieStreamer::Init(const TArray<FString>& MoviePaths, TEnumAsByte<EMoviePlaybackType> inPlaybackType)
{
//
// Initializes the streamer for audio and video playback of the given path(s).
// NOTE: If multiple paths are provided, it is expect that they be played back seamlessly.
UE_LOG(LogMoviePlayer, Log, TEXT("FAVMoviePlayer init. Path count = %d..."), MoviePaths.Num());
// Add the given paths to the movie queue
MovieQueue.Append(MoviePaths);
// Play the next movie in the queue
return StartNextMovie();
}
而且,在XXXMovieStreamer::Init的时候, 就会把对应的视频播放.
网友评论