World Composition and main menu - not what Epic planned for? Lots of "bypasses"?

I’ve been trying for the past 2 days to get a world composition level to work together with a main menu, and it all seems very awkward and not straight forward.

What I’m trying to achieve:
(1) Have a level that is simply a widget - the main menu - with a “new game” button
(2) Have my entire game as a persistent level with world composition, and sub levels.

Sounds simple, but:
(1) When loading the persistent wc level, it will show the player on an empty space, until it loads the sub level needed
(2) It sounds like a good idea to pause the game until the sub level is loaded, but if you pause it, it won’t even be loaded at all
(3) There doesn’t seem to be any way to be notified when the sub levels has finished loading; so even if you do want to show a loading screen, when will you stop showing it?

At the moment I’m trying to do the above using some cpp functions that calls functions on World such as IsVisibilityRequestPending(), but it all just seems a bit awkward, as if it’s not the way Epic has intended to use it.

What is the way then? or is wc together with a main menu just not something Epic ever planned of?

If you have CPP there are specific functions s to use for the load percentage or status. You just need to look those up.

In BP, you have other ways to do this.
the easiest is to just pop in a delay for however long you think it needs plus another 2 seconds or so.

the hardest is probably to expose the CPP functions for the status of the load to BP and use those.

12:42
answer delayed till 36m in

Using a delay for how long I think the loading is gonna take? that’s absurd. Even if one could guess that, it can changes drastically from one computer to the other. And there isn’t a way to guess that.

I tried to look at the video, but from what I can tell most of it is irrelevant since it’s not using world composition, so he can decide what to load and when. World composition will automatically load levels based on distance, and you can’t load/unload any levels manually.

This needs a dedicated video or tutorial from Unreal devs, there are lots of confusion on how to handle it properly here too.

He gives you a node you can use right at 36m. Islevelloaded I think.
just toss up the widget, leave it on until that node returns true.

[USER=“3140864”]MostHost LA[/USER] - you’re ignoring the fact we’re talking about wc though; we don’t have a reference to any level or name; the idea is that the wc will load the levels automatically, so you shouldn’t hard code any of them either.

Imagine the game is starting, and the character start position is in bounds of level C4. There’s a chance the wc will load some levels around C4 if it’s small enough, so let’s assume it will load all following levels: C3, C5, B4, B6 (top/bottom/left/right of C4) + C4. So we can’t even wait until the 1st level is loaded since we don’t know how many of them. And we don’t want to hardcode ‘C4’ either.

When you start the game, only the persistent level is loaded, and when you have landscapes in levels, it takes time to load them. If player is allowed to move (physics, not input) - there’s a high chance character will fall before the landscape has loaded.

As far as what I know from implementing things, Cpp has nodes to get what the status of the loading is.

But that’s unrelated anyway. You shouldn’t rely on unreal loading the levels you need.

You just do a position check to find out where the player is at, and you load the appropriate level(s) based on whatever algorithm you wish. (Usually detect player position percentage on bounds and maybe load different levels as well if over 50%).

you do this commonly because resuming a game from a load file you would have to re-load the position and relevant states.

So, you do want to hard code, but you aren’t hard coding the level, you are hard coding a system that tells you what level is relevant.
alternatively you can cheat and save the name of the loaded levels on save so you can then re-load them.
but that doesn’t cut it for starting a level. So you would need to pass in the level names you want loaded to that function when starting from scratch.

If you open the engine source and look at it, most of work is done for you. If you are stuck to BP that’s a separate story (and saving level names isn’t a bad idea).

The other thing to consider is multithreading, but that’s a whole different basket of theory. Which is important when dealing with async loading though.

Also, in world composition you have streaming distance layers. These could potentially complement the manual loading process. Manually load 4 levels the player sits on/near and world composition should take care of the rest by distance - including distance mesh replacements.

The proper way to address the player falling and getting stuck through loading objects is exactly a manual load.
stop the player from moving, check if the relevant level under him is loaded before re-enabling movement.

You don’t even have to manually load, you just need to resolve the position to a level name which you can do by way of a CVS file with distances, and query if it is loaded or not via the branch…

[USER=“3140864”]MostHost LA[/USER] I don’t think you’ve played enough with world composition by the way you talk.
When you use world composition you CAN NOT load the levels manually. This is not about my decision, this is how world composition works.
You can’t add/remove levels to it either, and you can’t change the “streaming method” like you do when you use a level with sub levels (without world composition).
So most of what you said really has no meaning.

BUT:
You mentioned resolving the position to a level name. If that would be possible, I could query the IsLevelLoaded() method and pass it to it, but I couldn’t find anyway to find the current level based on the player position.

I ended up writing the following cpp function and exposing it to BP, but as stated in the initial post it seems like this function should be made by epic. I fail to see any other way to do it.



ULevelStreaming * FindPlayerLevelStreaming(const APlayerController *PlayerController)
{
    if (!GEngine || !PlayerController)
    {
        return false;
    }

    UWorld *World = GEngine->GetWorldFromContextObject(PlayerController, EGetWorldErrorMode::LogAndReturnNull);
    if (!World)
    {
        return false;
    }

    FVector ViewLocation(0, 0, 0);
    {
        FRotator ViewRotation(0, 0, 0);
        PlayerController->GetPlayerViewPoint(ViewLocation, ViewRotation);
    }

    FIntPoint WorldOriginLocationXY = FIntPoint(World->OriginLocation.X, World->OriginLocation.Y);
    UWorldComposition::FTilesList &TileList = World->WorldComposition->GetTilesList();

    for (int32 TileIdx = 0; TileIdx < TileList.Num(); ++TileIdx)
    {
        FWorldCompositionTile &Tile = TileList[TileIdx];

        FIntPoint LevelPositionXY = FIntPoint(Tile.Info.AbsolutePosition.X, Tile.Info.AbsolutePosition.Y);
        FIntPoint LevelOffsetXY = LevelPositionXY - WorldOriginLocationXY;
        FBox LevelBounds = Tile.Info.Bounds.ShiftBy(FVector(LevelOffsetXY));

        LevelBounds.Min.Z = -WORLD_MAX;
        LevelBounds.Max.Z = +WORLD_MAX;

        //
        if (LevelBounds.IsInsideOrOn(ViewLocation))
        {
            return World->WorldComposition->TilesStreaming[TileIdx];
        }
    }
    return nullptr;
}


The loading systems aren’t mutually exclusive. You can and often do use both to stream in other things, like interiors perhaps.

18:36

If you’re talking about using a non-distanced layer, it is mutually exclusive, in a way. That is, once you assign a level to a non-distanced layer, world composition won’t automatically load it which means you can either use automatic loading or manual loading, you can’t use both on the same level (hence mutually exclusive).

And obviously disabling the automatic loading isn’t a good answer in this case, since the entire question was how to combine them both: world composition (automatic loading) and loading screen.

You can use both at the same time without issues.

You can run one manually, while the map loads on it’s own via world composition.

What’s more, there are a couple of projects out there that implement multithreading in C++ you could refer to for how things should be done. I believe the sample RPG one does so. Ofc. Doesn’t use world comp, but the code to load should all be included…

I haven’t played enough with the manual loading to know it, but even if it’s true I still don’t think that’s a good way to know.
The entire point of using world composition is it will loads the maps automatically for you based on distance; there’s no reason you should find the level the player is on and load it self, you’ll just be doing the world composition job.

Also, I couldn’t find any way to find the level the player is at (excluding the method I wrote above, but again, it seems like something that should of been at the Engine rather than the Game)

I’m also not sure how multithreading is related to the conversion. I’m also familiar a bit with the ActionRPG sample, and the only multithreading it has is about saving/loading game slot, which actually calls a method on UGameplayStatics that does an async task on another thread, and even then it has nothing to do from levels. It also uses a simple level with 2 sub levels that are always streamed in, so nothing relevant in there too. Unless I’m missing something?

There’s a talk from from epic that covers the loading and multithreading. You may want to give it a look as it clarifies some stuff and probably references the actual project (which may not be the rpg? I’m 90% sure it was, but I don’t recall tbh).

in short, you want to set up a proper multithreading system (which i would assume both level streaming nodes and world comp do, but you really never know unless you poke in the source). To ensure Async loading of levels and the management of the transitions.

Maybe we take multithreading for granted, since if you weren’t able to compute several things at once you would have crazy slow loading times… really not sure. I know that implementing your own custom c++ saves you a lot of trouble.

I think we all agree that the system built by epic could be improved upon - one thing everyone complains about and even released games have is the texture streaming causing visible pops after/during initial load.
like you said, there is no way to know how long a delay should last ad the time it takes for the proper textures to pop up will vary depending on the PC or system in use (can always bench the lowest end you have and go with that time delay though. People with better pcs just wait).

Re the manual loading. It’s kind of important and you should poke around with it.
mostly, as I mentioned, you use it to load interiors. Generally with a streaming volume or some such. Depending on things occlusion is just not enough to get rid of stuff you don’t see. So being able to load/unload the clutter and insides of a house can be a good performance improvement. Would all depend on your mem/cpu budget too, and it is something you can and should refine once the level has been setup properly… though setting up the underlying system for it and just building into it might end up saving you time.

And on doing the world composicomp’s job. Yes, well, if you want to optimize things you’ll have to anyway. In fact, whatever you write may end up being far more reliable then the built in system since it would be specific to your use case. That’s why I mentioned that you can literally take the code from Epic and re-work it for your needs…

My suggestion is to store the current level name at load. You can then easily perform other operations as well as check if it is loaded.
your code works for finding a name, but just committing the name to memory in the first place would be better.

Again, can epic produce better ways to do this? Yes.
Will they? Probably not…
I think you should drop that part of the argument and just figure out what best works for your project (not you, each project will need different load/i/o handling).