Download

How to save actors in a streamed Level?

Hi there,

I’m curious what will be the best approach to save actors in a streamed level? For example, if an actor was destroyed during gameplay (not just disabled the visibility), how can I ensure that the actor is still destroyed next time the play enters the steaming volume again? Not using Levelstreaming for collectable actors in an open world context would more or less break the game, I’ve made a test with 5000 collectables in a testscene and the fps drop was unacceptable.

Can anybody help me how I can solve that issue with a hint or two?

Ok, maybe I should ask different - How can I get reference to actors in a streamed level?

UGameplayStatics::GetStreamingLevel gives me a refernece to a streaming level by name, but I can’t access any actors in the level itself. UWorld()->GetLevel() has an actors array but nothing similar seems to exist for a streamed level.

I think the best approach is to save a reference to them somehow, then have them check it on begin play to see if they should exist or not.

For example, you have a list of collected item ID’s stored somewhere.
On BeginPlay, the item gives itself a unique ID somehow (maybe Location XYZ or something).
When the item is collected, it adds it’s ID to that collected item ID list.
And also in it’s beginplay, the item checks if it’s ID is in that list and if it is, it destroys itself.

This way there’s a list of all collected items, and they will automatically remove themselves as soon as the streaming levels load.

I’m pretty sure streamed levels just load in as-is from the content browser, so you’ll have to make changes to it’s actors as soon as they load in, and can’t modify the level itself directly otherwise.

Do you mean to give and store a unique ID for every item in a data table after the item is placed in the world? The only problem I see with it is the game itself, is an open World RPG like Gothic 2 and it will require a list with 100000+ item actors in the exterior world to store and probably even more in interiors (every interior of a dungeon or house is it’s own map that will be loaded by a load door). Giving then a unique ID and putting them in a data table by hand would slow down the cluttering of locations very much I fear.

You don’t have to do it by hand. You can generate unique IDs, for example by using the FGuid struct (then store data in a TMap using the Guid as key) or by making a tool that automatically assigns a unique index of some description.

In my current project, I’ve solved a very similar scenario by using a spawner manager. There’s one of them in each streamable level, and I’ve overridden its CheckForErrors() method, in which it automatically assigns index numbers (unique and consecutive) to any spawners that have not yet been set up properly. So, I just have to run a map check in the editor to set everything up, and the data is stored in TArrays, using the item index number, for ridiculously fast look-ups.

Thanks Xenome and Juice-Tin,

I generate an unique ID now using FGuild which works fine. For testing purpose I generate the ID for now in the BeginPlay override, however this is a very bad place to stay here.

@Xenome: I guess I’ll remove the ID generated out of the BaseClass for my Collectable Actors and move it into a manager actor, makes much more sense I guess. I would like to give the TMap the StreamingLevel name as key and the ID as ElementType…however I need to look before if it is possible to get the name of a streaming level, currently I only know it works for the persistent map. LevelStreaming is something complete new for me. Maybe also a TMulitMap is a good choice since I could also store a bool as state (destroyed or not).

You don’t have to generate any fancy ID.
Actors names = Unique ID.

If you hover the mouse over an actor in Editor it will display as a Tooltip the actual Actor Unique ID (its in Level name).

Are you sure that the same ID Name of an Actor in the editor can’t exist on two or more different maps (not a streaming map)…like in the exterior World and maybe in a cave interior which is a complete independent map where the player will be teleported.

Nope; I believe actors ID are bound to their owner Level (World).
So if you have more than 1 level loaded proly actors may have same name in each Level.

Yes, I thought the same, but the FGuid looks pretty bulletproof unique to me, at least doubling seems pretty unlikely. I use a system for entering interiors similar as Bethesda is using it since TES3:Morrowind, basically every interior is it’s only map, so relying on GetName() will provide a high risk of doublings if it comes to storing collected item data.

I’m still not sure how to store the initialized IDs for the item actors, if it would make sense to write them to a binary file, but I’m not sure if it will work during a mapcheck anyway. I would prefer storing in a DataTable, but as far as I’m aware I can’t add entries to a data table more or less “automatically”, looking at UDataTable I see no option to add a row by c++ code, however maybe I just missed something.

I’ve just managed to make it work in my plugin; using Actor names.
All I had to do was tell Unreal to track which Level “package” the Actor comes from; so even though a bunch of collectible Actors have the same FName when added to every Level to be loaded by the Streaming system, my “SerializeWorld()” function knows from which Level each of those Actors comes from so it destroys the one that have been collected from the correct Streamed (or persistent) Level.
Internally Unreal stores an Streamed Actor somewhat like this “/Game/AssetPaths/LevelName.LevelName**:**PersistentLevel” / ActorUniqueName