World Partition - actors added at runtime are never unloaded

Hey!

So I guess actors that are created at runtime seem to never unload, even when they are set to be spatially loaded.

I ran a test, placing the same actor in Editor mode and in runtime, and those added in runtime (either using game mechanics, which means adding some referenced actors AND just dropping actors from content browser in runtime) will never unload, even when placed just side by side to the actor placed in editing mode.

How do I fix this? I’m working on a game with construction mechanics and I kind of WANT the buildings to unload when I leave the base. I know how to bring them back using save system, but I don;t want them in my memory all ethe time.

Anybody know how to fix this? I can think of a workaround - like an actor that is placed on each partition during editing mode that fires an event dispatched when it’s unloaded so all the runtime-placed actors are unloaded too, but this sounds like a hassle I would love to skip…

Any1? Thanks!
PS

what do you mean by unload? do you try to remove them and they don’t get destroyed by unreal, they remain in memory?

if yes check if something is referencing them and keep them in memory because of this

I want them to unload together with the partition they are placed on. This usually mean that they fire the On End Play event and are destroyed.

Then, when I’m back at the said partition and it loads again, the editor-placed actors will usually be there which is fine, and I would need to re-spawn those runtime-placed actors abck to the level and I can do that.

But I need the runtime placed actors to be destroyed first.

To test it, I created an actor with just this functionality

and placed one in editing mode, then in runtime I dragged and dropped one to the level. Then I moved away to unload the partition they were both on. The one placed in runtime never unloaded.

It references no other acor and I have no idea what can reference the test actor since it has no variables…

They will be unloaded, for example, if their Owner (a spawn parameter) is some actor which is unloaded

Probably the owner. Meaning whatever created the actors is still present and automatically set to “own” the actors.
If you change the owner to be the level (or something within that level) they will probably unload.

Either way, im rather sure you are doing this wrong.

First of all, in all likelyhood these “things” should not always be BP actors; An instanced static mesh (like foliage) will keep their transform and automatically optimize addition/removal for visibility.
if they are moving actors needing to move while the game progresses, that may need to be different, still wouldnt have them always present as BPs

Second, you can probably just swap the BP in/out based on distance from the player, which is basically how you do anything and preserve performance.
Meaning there may not be any need to unload it with the level when you can unload it after say 50m from the player, for instance.

Third, the remove/respawn presents an impossibility if you don’t have something at the place which you want to use for the spawn point. See point 1.
Essentially without an actor or component of some kind you cannot check for distance, or do anything much. Because you do need this distance, like mentioned by point 1, its best to come up with some sort of replacement system (point 2) that works off some other object.

If you better explain what you are doing we can give you more ideas, but as it stands your concept is too vague to guess at what you really want to do - or a best solution for it.

OK, thanks for the answer!

Well, the owner is something I never considered, I’ll look into that, thanks!

As for the instanced meshes etc, that would be hard, as the buildings I’m talking about are quite complicated by now with lots of functionalities (crafting stations, mesh-by-mesh construction style etc). Reworking them in the way that they are just mesh representation would be very very time consuming. But for other simpler stuff (plants and fields) I actually do just that - there’s an actor managing interactions, but the meshes are all instanced.

This is something we settled for - we have bunch of lightweight actors that unload together with said partition and run the unload functionality :slight_smile:

I have same issue, actors spawned during runtime do not get unloaded, did u found reason why is it happening? I cant believe there are literaly no info about something as simple as world partition not working for actor that is spawned during runtime

Hi guys :slight_smile:
I recently faced a similar challenge. To resolve this, I created logic to add actors to level streaming cells using C++. I thought I’d share the process I used, along with the key functions involved, to help anyone else who might be dealing with a similar issue.

Overview of the Process

  1. Identify the Actor’s Level:

    • Each actor in Unreal Engine is associated with a specific level, whether it’s a PersistentWorld or streaming level. We first need to determine which level the actor belongs to. This is done using methods like AActor::GetLevel() on the actor.
  2. Check for Existing Level Streaming:

    • Before assigning a level streaming, we need to check if the level that the actor is already assigned is a streaming level. The function UWorld::GetLevelStreamingForPackageName() checks for an existing streaming level corresponding to the actor’s level package name.
  3. Iterate Through Streaming Levels:

    • If actor’s level is not a streaming level, we need to iterate through all currently loaded streaming levels in the world. This can be accomplished using UWorld::GetStreamingLevels(), which returns an array of all streaming levels.
  4. Determine Bounds of Each Streaming Level:

    • For each streaming level, we need to retrieve the bounds of the world partition cell. This is done using ULevel::GetWorldPartitionRuntimeCell() and IWorldPartitionCell::GetCellBounds(). The bounds represent the area in which the cell exists and will be used to check if the actor is within that area.
  5. Check if Actor is Inside Bounds:

    • Using the FBox::IsInside() function, we can determine if the actor’s location falls within the calculated bounds of the streaming level. If the actor is inside, it’s eligible to be added to that level.
  6. Add Actor to the Level:

    • If the actor is not already present, we can add it to the ULevel::Actors list. Using a method like AddUnique() ensures that we do not add the same actor multiple times.

Functions You Might Use

  • AActor::GetLevel(): Retrieves the level that the actor is currently a part of.
  • UObject::GetOuter(): Used to access the outer object of the actor, which helps in getting the package name of the level.
  • UWorld::GetLevelStreamingForPackageName(FName): Checks if a streaming level is already assigned.
  • UWorld::GetStreamingLevels(): Returns an array of currently loaded streaming levels in the world.
  • ULevel::GetWorldPartitionRuntimeCell(): Accesses the world partition cell associated with the level.
  • IWorldPartitionCell::GetCellBounds(): Retrieves the bounding box of the world partition cell.
  • FBox::IsInside(FVector): Checks if a point (actor’s location) is inside a specified bounding box.

Note: If there are no actors placed in certain regions of your map during edit time, the World Partition system won’t generate streaming cells for those areas during the cooking process.

Hope this helps :slight_smile: