Download

Async Load Map

Hi there.

I would like to asynchronous load my Map.




DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLoadingScreenLoadedSignature);

UCLASS(BlueprintType,Blueprintable)
class ALoadingScreenMode: public AGameModeBase
{
    GENERATED_BODY()
public:
    UPROPERTY(BlueprintAssignable, Category = "Level Loaded")
        FLoadingScreenLoadedSignature OnLevelLoaded;
private:
    FStreamableManager Loader;
    FStringAssetReference MapToLoad;
protected:

    void PostInitializeComponents() override;
    void BeginPlay() override;
    void Tick(float DeltaTime) override;

private:
    void LoadComplete();
    void LoadLevel();

};





void ALoadingScreenMode::PostInitializeComponents()
{
    Super::PostInitializeComponents();
    FString MapString = UGameplayStatics::ParseOption(this->OptionsString, "MapToLoad");
    if(MapString.IsEmpty())
    {
        MapString = "/Game/Maps/TitleScreen/TitleScreen.TitleScreen";
    }
    MapToLoad = FStringAssetReference(MapString);
}

void ALoadingScreenMode::BeginPlay()
{
    Super::BeginPlay();
    Loader.RequestAsyncLoad(MapToLoad,FStreamableDelegate::CreateUObject(this,&ALoadingScreenMode::LoadComplete));
}


void ALoadingScreenMode::LoadComplete()
{
    OnLevelLoaded.Broadcast();
    FTimerHandle handle;
    GetWorldTimerManager().SetTimer(handle,this, &ALoadingScreenMode::LoadLevel, 1.0f, false);
}

void ALoadingScreenMode::LoadLevel()
{
    UGameplayStatics::OpenLevel(this, FName(*MapToLoad.GetAssetName()));
}

However [FONT=courier new]OnLevelLoaded.Broadcast(); is fired, but then the Level is loaded as usual (blocking, freezed screen), probably because of OpenLevel(); …?

Are there any solutions?

Not sure if this might help: How do I use PrepareMapChange/CommitMapChange? - UE4 AnswerHub

Hey there, thanks for the reply.
But isn’t that Level Streaming? I don’t want to use Streaming, I just want my Level to Load async, and open that level once loaded.

As stated earlier, I am searching for a solution without Level Streaming.

There is no way to load a map without Streaming or Servertravel. In UE4 there have to be allways a map to be loaded. Look at the documentation under: Seamless travel. There you find a lots of Information what is possible and what not.

And if you use openlevel (it doesn’t matter in Blueprints or c++) the engine Task stops and load that Level.

So the level streaming stuff is basically an asynchronous load of a level and I have set this up in one of our games so that we can have an animated loading area.

At the moment, when I call my function to load a level, I send the user to a loading area/screen. I then call the function to unload my current level and use a function in the LatentActionInfo to be called when the task is complete.



const FName* const LevelNamePtr = //Current level Name;
            if (LevelNamePtr)
            {
                FLatentActionInfo LatentInfo;
                LatentInfo.CallbackTarget = this;
                LatentInfo.ExecutionFunction = "UnloadCurrentLevelFinished";
                LatentInfo.Linkage = 1;
                LatentInfo.UUID = 1;

                UGameplayStatics::UnloadStreamLevel(this, *LevelNamePtr, LatentInfo);
            }
            else
            {
            }

Once it has hit my function for unloading, I then begin the call to load the next level.


const FName* const LevelNamePtr = // Name of new level to load;
        if (LevelNamePtr)
        {
            FLatentActionInfo LatentInfo;
            LatentInfo.CallbackTarget = this;
            LatentInfo.ExecutionFunction = "LoadLevelFinished";
            LatentInfo.Linkage = 1;
            LatentInfo.UUID = 1;

            UGameplayStatics::LoadStreamLevel(this, *LevelNamePtr, true, false, LatentInfo);
        }
        else
        {
        }

When the final callback is hit for LoadLevelFinished, the map is auto-shown due to the bMakeVisibileAfterLoad from LoadStreamLevel and I can just hide my loading screen/leave my loading area.

Use LoadPackageAsync for precaching the map. Then you will need to prevent it from being garbage collected (AddToRoot or save it to a UPROPERTY raw pointer, etc.). Then use OpenLevel and it will find your precached package.

thx Sveitar, LoadPackageAsync helps a lot.
I implemented my loading by using it, it’s much better than level streaming in most cases

can you share some code on how you did it?

Hi, I am using LoadPackageAsync and add the loaded UPackage to Root using AddToRoot(), it works fine in Editor, but no luck in packaged game, could you please share some code point me out? Thanks in advance.

Would love to hear how you did it with LoadPackageAsync! trying to figure this out too!

Hey there!

@xiuxiu, @Sveitar I would be interested too. Can you tell us more on the implementation?

thanks!

Hi there,
I am going to share my solution here. I hope it can help anyone who is facing async loading level issue.

By the way, there are 2 common ways to load level async. The first one is using LoadStreamLevel. The limitation is I can’t use another gamemode. Therefore, it can’t be work on transitting level from Main Menu to In-game level. As a result, **LoadPackageAsync **is another way.

My situation is trying to change the level when player trigger the “StartGame” action. The game will show a loading widget, at the same time it will load the next level async. Once the level is loaded, I will wait for 3 more seconds for preheat.

Here is the part of my PlayerController CPP



void AMyPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();

    // Bind Action
    InputComponent->BindAction( "StartGame", EInputEvent::IE_Pressed, this, &AMyPlayerController::OnStartGameClicked );
}

void AMyPlayerController::OnStartGameClicked()
{
    // Using LoadPackageAsync to implement the loading screen
    LoadPackageAsync( TEXT( "/Game/Levels/MainGame_Level" ),
    FLoadPackageAsyncDelegate::CreateLambda(  = ] ( const FName& PackageName, UPackage* LoadedPackage, EAsyncLoadingResult::Type Result )
    {
        if( Result == EAsyncLoadingResult::Succeeded )
        {
            OnLoadPackageSucceed();
        }
    } ) ,
    0,
    PKG_ContainsMap );    
}

void AMyPlayerController::OnLoadPackageSucceed()
{
    // cLoadTimerHandler is a FTimeHandler
    GetWorld()->GetTimerManager().SetTimer( cLoadTimerHandler, this, &AMyPlayerController::OnPreHeatFinished, 3.0f, false );
}


void AMyPlayerController::OnPreHeatFinished()
{
    UGameplayStatics::OpenLevel( this, TEXT( "MainGame_Level" ) );
}


For now, I am still studying how to implement FLoadPackageAsyncDelegate in a better way. If you guys have any idea, please let me know :slight_smile:

If you want to catch the delegate in a separate function you can do as follows:



LoadPackageAsync( TEXT("LevelName"), FLoadPackageAsyncDelegate::CreateUObject(this, &UMyClass::MyFunction), 0, PKG_ContainsMap);


Cheers.