How to Check If Level Instance Inside BPLI Is Loaded?

I am creating a procedurally generating level, where I have a main Blueprint called “BP_LevelGenerator” that generates chunks - using a known chunk’s exits to get a new chunk’s spawn location, pretty simple logic. The chunks are basically just Blueprints that are made using Unreal Engine’s built-in Level Instance Blueprint (BPLI).

My issue is that I want to have the ability to know when all of the BPLIs on my level have been fully loaded. This would make it possible to hide any loading stutters behind a loading screen.

So far I’ve created an interface “Chunk Fully Generated”, which the “BP_LevelGenerator” would call on all generated chunks each X seconds. If all chunks have been generated - all returned false on the interface call - then I could safely remove the loading screen.
But this has not been successfull and I am basically stuck. The loading check should be happening in the chunk classes’ “Chunk Fully Generated” interface implementation.

Below is a parent blueprint that the rest of the BPLI chunks are children of. The Level Instance Component hosts a specific Level Instance (LI) that needs to be verified.

Any ideas on how this could be achieved? Any help is welcomed as I’ve spent the last 2 months trying to find a solution to this.

(EDIT: spelling)

Level Instance Blueprint? Do you mean Packed Level Blueprint? If so, then that class handles the loading. If you’re using C++, you can tap into the loading state by adding a delegate via FLevelStreamingDelegates. I think you can use OnLevelStreamingStateChanged and the NewState should be ELevelStreamingState::LoadedVisible.

If you were using a straight up Level Instance, you can do something like this:

  bool Success = false;
  FRotator rot = LevelActor->PlatformRotation;
  FVector PlatformPosition = LevelActor->PlatformLocation;
  ULevelStreamingDynamic* DynamicLevel = ULevelStreamingDynamic::LoadLevelInstanceBySoftObjectPtr(world, this->PreviewLevelInstance, PlatformPosition, rot, Success);
  if (!Success)
	return FAIL;
  FScriptDelegate D;
  D.BindUFunction(this, FName("PostLoadLevel"));
  DynamicLevel->OnLevelShown.Add(D);

You’ll need a UFUNCTION called PostLoadLevel, or change the FNAME in BindUFunction. You won’t know which level loaded though. But if you just care about the number of levels loaded, you can use it to update your counter.

If you’re in blueprint only, I don’t know of any way with Level Instances since it doesn’t give you the dynamic level instance reference. For Blueprint Packed Levels, I think it’s even more restricted even in C++. But it’s quite possible I’m wrong. I just haven’t seen it yet. From what I know, ULevelInstanceComponent was originally an editor only component. But it seems they reused it with Packed Level Blueprints.

Note that this is all for Level Streaming Mode. If you’re using embedded mode, that’s a whole other ballgame.

If you’re talking about different kind of blueprint, let me know. I’ve done tons of testing on loading levels, level streaming, etc.

Hello! I’ll try this later, possibly today if I have enough free time. I’ll let you know

I think I solved this. I just created a new UActorComponent derived class and made its functions discoverable for Blueprints. In the boolean function IsLevelInstanceLoaded I first check whether the Level Instance is loaded into the memory at all. After that I check whether the level instance is already streamed (AKA visible).

bool ULevelInstanceLoadWatcher::IsLevelInstanceLoaded() const
{
    UWorld* World = GetWorld();
    if (!World) return false;

    ULevelInstanceSubsystem* Subsystem = World->GetSubsystem<ULevelInstanceSubsystem>();
    if (!Subsystem) return false;

    ILevelInstanceInterface* LevelInstance = Cast<ILevelInstanceInterface>(GetOwner());
    if (!LevelInstance) return false;

    // First check: is it loaded into memory at all
    if (!Subsystem->IsLoaded(LevelInstance)) return false;

    // Second check: find the actual streaming level and confirm it's visible
    // (Loaded != Visible - the level goes through MakingVisible after load)
    ULevel* LoadedLevel = Subsystem->GetLevelInstanceLevel(LevelInstance);
    if (!LoadedLevel) return false;

    for (ULevelStreaming* StreamingLevel : World->GetStreamingLevels())
    {
        if (!StreamingLevel) continue;

        if (StreamingLevel->GetLoadedLevel() == LoadedLevel)
        {
            return StreamingLevel->IsLevelVisible();
        }
    }

    return false;
}

I am sure there is a better and more optimal way to do this, as I am not an expert in UE5 coding. If someone knows a better way, please reveal it! :smiley:

I had assumed you wanted to be notified when it was loaded. FLevelStreamingDelegates is what you want. But if you’re fine using a polling function, great.