美文网首页
UE4 Loading screen 模块

UE4 Loading screen 模块

作者: 我真的不知道该起什么名字了 | 来源:发表于2019-06-21 16:13 被阅读0次

    本文参考:
    https://github.com/ue4plugins/LoadingScreen
    https://wiki.unrealengine.com/Loading_Screen

    因为openlevel是阻塞式加载场景, 为了在切地图的时候,有loading图, 所以需要异步线程显示loading图.
    LoadingScreen 就是做这个工作的.

    使用loading screen时, 必须注意以下几点:

    1. 必须在launch game模式下运行.


      image.png
      image.png
    2. 必须关闭Pre-Loading Screen Movie Player插件


      image.png
    3. 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的时候, 就会把对应的视频播放.

    相关文章

      网友评论

          本文标题:UE4 Loading screen 模块

          本文链接:https://www.haomeiwen.com/subject/ytdsqctx.html