Custom WorldPartitionBuilder Commandlet loads actors with inconsistent collision

We have a custom WorldPartitionBuilder commandlet which loads regions of the map one at a time, and performs some collision queries to find actors and surface info, and uses this info to perform some operations. It then unloads that region and moves to the next.

What we are seeing is that the FLoaderAdapterShape created with CreateEditorLoaderAdapter loads the region as we expect, loading several thousand actors at a time, but in some regions of the map the loaded actors don’t seem to have collision. In other regions they do.

We are seeing some areas where our overlap queries are returning many actors, but in other regions they only return a few despite many being loaded. The weird part is that its not 0, but a random selection on the order of 10 or so actors. There doesn’t seem to be any pattern either. Even the same mesh used hundred of times in the region will only return a single instance.

Importantly, it is consistent which actors the query finds. So its always the same 10 or so actors, but there are many more that should be found by the query.

Things we have tried:

- Ensuring that loading and actor registration has finished using FWorldPartitionHelpers::FakeEngineTick, FlushStreaming, and BlockTillLevelStreamingCompleted

- Setting the commandlet InitializationValues to include CreatePhysicsScene and EnableTraceCollision.

- Increasing the bounds of the loader adapter

While FakeEngineTick is required for any collisions to be registered before our queries in any region, it still leaves this random pattern of some actors not having collision.

I don’t think its related to data layers, as the use of data layers is about the same across the map, and there is no pattern to which actors the collision query returns and the actors data layer assignment. Also, the actors are loaded but seemingly don’t have collision.

We are also not using level instances.

This tool can also be run in editor manually, and in this case the collision queries are consistent across the entire map. It is using the exact same LoaderAdapter logic in the editor case as well. The only difference I can think of is the auto-ticking of the world in the editor viewport.

Any thoughts on why some actors would not be registering their collision in this case would be welcome. Please let me know if the issue isn’t clear, its a fairly complex setup which I’ve done my best to describe.

Hello!

Can you share the custom builder code that takes care of loading the regions?

Martin

Hi Thomas,

Flushing the async compilation of static meshes seems appropriate with the problem you described. This is a common cause for that type of problem when dealing with commandlet\editor .

Regards,

Martin

Hi Martin!

Sure, this is all in a loop iterating over some loaded box volumes (That’s CurrentZone here). The various load blocking and flush streaming calls don’t seem to change anything. Likewise I included the GC call and extra ticks after unloading although those don’t make any difference either.

Its also worth mentioning that when this is run on a single zone instead of iterating each zone in the world, the query results are the exact same. So I don’t think the issue is related to subsequent loads.

// For each Zone (a box shape in the world, always loaded)
FBox ZoneBounds = CurrentZone->GetZoneBounds();
UWorldPartitionEditorLoaderAdapter* Adapter = World->GetWorldPartition()->CreateEditorLoaderAdapter<FLoaderAdapterShape>(
		World,
		ZoneBounds.ExpandBy(5000.0f),
		TEXT("..."));
Adapter->GetLoaderAdapter()->SetUserCreated(false);
Adapter->GetLoaderAdapter()->Load();
 
World->GetWorldPartition()->FlushStreaming();
World->BlockTillLevelStreamingCompleted();
		
FWorldPartitionHelpers::FakeEngineTick(World);
 
// Collision queries performed here
...
 
if (Adapter && Adapter->GetLoaderAdapter())
{
	Adapter->GetLoaderAdapter()->Unload();
	World->GetWorldPartition()->ReleaseEditorLoaderAdapter(Adapter);
	Adapter->ConditionalBeginDestroy();
	Adapter = nullptr;
}
 
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
FWorldPartitionHelpers::FakeEngineTick(World);
 
// Continue to next zone

I believe I have found a fix for this. It seems that the issue is async nanite mesh compilation, which skips collision registration until completed.

Calling this after loading and before a final FakeEngineTick gives correct results from collision queries.

FStaticMeshCompilingManager::Get().FinishAllCompilation();