truong-bui / AsyncLoadingScreen

Async Loading Screen is a free and open-source plugin for Unreal Engine. Async Loading Screen allows you to easily configure a Loading Screen System in the project settings, and automatically add a Loading Screen whenever you open a new level, without Level Streaming.
https://www.unrealengine.com/marketplace/en-US/product/async-loading-screen
MIT License
787 stars 99 forks source link

Loading ending too early #3

Open EskelCz opened 3 years ago

EskelCz commented 3 years ago

Awesome project, thank you. Works great, with one exception though. My game is mostly based on Paper 2D sprites and their textures are not yet loaded when the loading finishes. So all the sprite assets are visibly popping in, which kind of defeats the purpose of a loading screen. :) Can it be fixed somehow?

truong-bui commented 3 years ago

You can set Minimum Loading Screen Display Time or for more correct timing you should enable Wait For Manual Stop, then manually call StopMovie in Slate thread, check my post on the plugin's topic

EskelCz commented 3 years ago

@truong-bui Sorry I'm a little confused, I'm not playing a movie in the loading screen, just showing a background image.

So for the proper timing I would need to change the plugin's logic in C++ to also account for texture loading and recompile it? I'm thinking that this might happen to other people as well, I guess it's a feature request then. But in general, shouldn't the plugin wait for everything to be loaded?

truong-bui commented 3 years ago

@truong-bui Sorry I'm a little confused, I'm not playing a movie in the loading screen, just showing a background image.

It works for both movie and background image.

So for the proper timing I would need to change the plugin's logic in C++ to also account for texture loading and recompile it?

Yes, you may have to do this, but I'm not sure if the result will be different.

I'm thinking that this might happen to other people as well, I guess it's a feature request then. But in general, shouldn't the plugin wait for everything to be loaded?

This can't be done at the moment. You should know that all game activities are blocked until level loading is completed, so asset loading in game thread only start after level is loaded. Also it's really hard to detect what is loading and when it's done loading. You may want to switch to Level Streaming instead

truong-bui commented 3 years ago

I'm thinking that this might happen to other people as well, I guess it's a feature request then. But in general, shouldn't the plugin wait for everything to be loaded?

Anyway, I'm thinking about the solution for this issue in the future update.

mossssi commented 3 years ago

Hey @truong-bui , Thanks for your great plugin. I just want a little guidance for setting this Manual Stop for my project. I know when to call StopMovie() but I don't know where should I write my code. Can I create a blueprint callable function and call it whenever I need?

truong-bui commented 3 years ago

@mossssi , you can write your code in SLoadingScreenLayout class, it is the class base for all other the layouts.

... Can I create a blueprint callable function and call it whenever I need?

No, you can't. You need to calll StopMovie() in Slate thread to able to manual stop the loading screen.

EskelCz commented 3 years ago

@truong-bui Now I've noticed that the issue actually is about level streaming. The assets that are loaded later are streamed in as a sublevel, I somehow forgot to mention. :) So I guess the question is slightly different, is it possible to wait for all initial streaming levels to finish loading?

Or a way to somehow expose the StopMovie function to blueprints?

truong-bui commented 3 years ago

Or a way to somehow expose the StopMovie function to blueprints?

Not possible at the moment. Also, I don't recommend using the plugin with Level Streaming. It's made with completely different purpose.

Swdanylenko commented 3 years ago

@truong-bui Now I've noticed that the issue actually is about level streaming. The assets that are loaded later are streamed in as a sublevel, I somehow forgot to mention. :) So I guess the question is slightly different, is it possible to wait for all initial streaming levels to finish loading?

Or a way to somehow expose the StopMovie function to blueprints?

yes, there's a way. onmapload rpc is fired by engine when persistent is loaded. that means too early for level streaming. you gotta implement own rpc and a system that checks if all streaming levels loaded. we did that in our project, but that still feels hacky. can't see other way tho.

Syphorlate commented 2 years ago

I ended up solving this issue with streaming levels by creating a simple HUD class and passing the loading screen slate widget shared pointer directly to it, seems to work well, except for a little stutter when it switches from movie player to hud(I recommend changing the text or so as it will be less noticeable). In my persistent level I use the Load Streaming level blueprint to load the relevant startup level(s) and then call StopLoadingScreen() after that is completed(with a slight timer based delay added).

Another interesting thing one could do with this is to smoothly fade the screen out over the loaded level.

here's the code for anyone interested, this is all that is needed:

UCLASS()

class ASYNCLOADINGSCREEN_API ALoadingScreenHUD :public AHUD
{
    GENERATED_BODY()
private:
    static TSharedPtr<SWidget> WidgetScreen;

public:
    static void SetWidget(TSharedPtr<class SWidget>& widget)
    {
        WidgetScreen = widget;
    }
    static TSharedPtr<SWidget> GetWidget()
    {
        return WidgetScreen;
    }

    virtual void BeginPlay()override
    {
        Super::BeginPlay();
        if (GEngine && GEngine->GameViewport && WidgetScreen.IsValid())
        {

            GEngine->GameViewport->AddViewportWidgetContent(WidgetScreen.ToSharedRef());
        }
    }
};

//Don't forget to declare the static var somewhere in the cpp to avoid missing symbols error...
TSharedPtr<SWidget> ALoadingScreenHUD::WidgetScreen;

// Also put this into your stop loading call which you can call from blueprint after streaming levels have loaded
void UAsyncLoadingScreenLibrary::StopLoadingScreen()
{
    GetMoviePlayer()->StopMovie(); 
    auto widget = ALoadingScreenHUD::GetWidget();
    if (widget.IsValid())
    {
        GEngine->GameViewport->RemoveViewportWidgetContent(widget.ToSharedRef());
    }
}

// This is the portion that goes into void FAsyncLoadingScreenModule::SetupLoadingScreen

    TSharedPtr<SWidget> widget;
    if (LoadingScreenSettings.bShowWidgetOverlay)
    {
        const ULoadingScreenSettings* Settings = GetDefault<ULoadingScreenSettings>();

        switch (LoadingScreenSettings.Layout)
        {
        case EAsyncLoadingScreenLayout::ALSL_Classic:
            widget = SNew(SClassicLayout, LoadingScreenSettings, Settings->Classic);
            break;
        case EAsyncLoadingScreenLayout::ALSL_Center:
            widget = SNew(SCenterLayout, LoadingScreenSettings, Settings->Center);
            break;
        case EAsyncLoadingScreenLayout::ALSL_Letterbox:
            widget = SNew(SLetterboxLayout, LoadingScreenSettings, Settings->Letterbox);
            break;
        case EAsyncLoadingScreenLayout::ALSL_Sidebar:
            widget = SNew(SSidebarLayout, LoadingScreenSettings, Settings->Sidebar);
            break;
        case EAsyncLoadingScreenLayout::ALSL_DualSidebar:
            widget = SNew(SDualSidebarLayout, LoadingScreenSettings, Settings->DualSidebar);
            break;
        }
        ALoadingScreenHUD::SetWidget(widget);
        LoadingScreen.WidgetLoadingScreen = widget;

    }

Also don't forget to change the default HUD in the world settings (alternatively simply extend your existing hud class to show the loadingscreen widget when needed).