Problem with LevelStreaming using C++

Loading a level at a given transform, this is used to load a level multiple times
at different locations.

bool UMyGameInstance::LoadLevelAtTransform(FName PackageNameToLoad, FTransform LevelTransform)
{
    // TODO: Add delegates
    ULevelStreamingKismet* StreamingLevel = static_cast<ULevelStreamingKismet*>(NewObject<ULevelStreamingKismet>(GetWorld(), NAME_None, RF_NoFlags, NULL));
  
    // Associate a package name.
    StreamingLevel->SetWorldAssetByPackageName(PackageNameToLoad);
    if (GetWorld()->IsPlayInEditor())
    {
        FWorldContext WorldContext = GEngine->GetWorldContextFromWorldChecked(GetWorld());
        StreamingLevel->RenameForPIE(WorldContext.PIEInstance);
    }
  
    StreamingLevel->LevelColor = FColor::MakeRandomColor();
    StreamingLevel->bShouldBeLoaded = true;
    StreamingLevel->bShouldBeVisible = true;
    StreamingLevel->bShouldBlockOnLoad = false;
    StreamingLevel->bInitiallyLoaded = true;
    StreamingLevel->bInitiallyVisible = true;
  
    StreamingLevel->LevelTransform = LevelTransform;
  
    StreamingLevel->PackageNameToLoad = PackageNameToLoad;
  
    FString PackageFileName;
    if (!FPackageName::DoesPackageExist(StreamingLevel->PackageNameToLoad.ToString(), NULL, &PackageFileName))
    {
        UE_LOG(LogGSStreamingLevel, Error, TEXT("trying to load invalid level %s"), *StreamingLevel->PackageNameToLoad.ToString());
        return false;
    }
  
    StreamingLevel->PackageNameToLoad = FName(*FPackageName::FilenameToLongPackageName(PackageFileName));
  
    // Add the new level to world.
    GetWorld()->StreamingLevels.Add(StreamingLevel);
  
    return true;
}

Loading an already existing streaming level, the tricky part is to get the level
instance, then you just have to kick loading in. This requires your levels to be in the level browser of your persistent level to work properly.

void UMyGameInstance::LoadLevel(const FName LevelName)
{
    ULevelStreaming* CurrentLevel = FindAndCacheLevelStreamingObject(GetWorld(), World);
    if (CurrentLevel)
    {
        // TODO: Add delegates
        CurrentLevel->bShouldBeLoaded = true;
        CurrentLevel->bShouldBeVisible = true;
        CurrentLevel->bShouldBlockOnLoad = false;
    }
}

ULevelStreaming* UMyGameInstance::FindAndCacheLevelStreamingObject(const FName LevelName, UWorld* InWorld)
{
    // Search for the level object by name.
    if (LevelName != NAME_None)
    {
        const FString SearchPackageName = MakeSafeLevelName(LevelName, InWorld);

        for (ULevelStreaming* LevelStreaming : InWorld->StreamingLevels)
        {
            // We check only suffix of package name, to handle situations when packages were saved for play into a temporary folder
            // Like Saved/Auto-saves/PackageName
            if (LevelStreaming &&
                    LevelStreaming->GetWorldAssetPackageName().EndsWith(SearchPackageName, ESearchCase::IgnoreCase))
            {
                return LevelStreaming;
            }
        }
    }

    return NULL;
}

FString UMyGameInstance::MakeSafeLevelName(const FName& InLevelName, UWorld* InWorld)
{
    // Special case for PIE, the PackageName gets mangled.
    if (!InWorld->StreamingLevelsPrefix.IsEmpty())
    {
        FString PackageName = InWorld->StreamingLevelsPrefix + FPackageName::GetShortName(InLevelName);
        if (!FPackageName::IsShortPackageName(InLevelName))
        {
            PackageName = FPackageName::GetLongPackagePath(InLevelName.ToString()) + TEXT("/") + PackageName;
        }

        return PackageName;
    }

    return InLevelName.ToString();
}

NOTE: All these examples are missing the level loaded/shown/unloaded/hidden delegates for clarity

1 Like