Replication of a dynamic spawn

I have question about replication. I spawn (raw, through code) a completely dynamic static mesh actor that uses default cube and is then set to replicate to all clients. The actor is very simple, it just 4 vertices so no LODs setup and it is non-nanite (99% of the rest of the level geometry is nanitized, except few rocks) and it is set to Movable. It’s NetCullDistance is the same as the rest of level meshes however, it disappears from client view/gets culled (?) way earlier than the rest of objects, almost 2x less range of even other non-native meshes. Does not appear to related to r.viewdistancescale either. I’ve tried setting bNetLoadOnClient upon spawn before replication onvoked but no effect.

Any idea what can cause it/where to look for the source of the issue?

World partition spatially loaded?


bNetLoadOnClient refers to replicated actors that already exist in the base level. It’s asking should the client load it or wait for replication subsystem (net cull distance etc).

The actor is not an asset in blueprint sense, it is default StatisMeshActor so technically it exist on both client and server.
Looking at Actor props. bIsSpatiallyLoaded is EDITOR_ONLY so it is not relevant in packaged build.

bIsSpatiallyLoaded is relevant to world partition. If an actor (meshes are actors) is spatially loaded and is in a cell that is unloaded, the actor will be unloaded.

To test increase the cell size and loading range in world partition. If the actor stays loaded longer (further distance), then it is spatially loaded.


Show your code for spawning the mesh. Also, What “class” is spawning it?

It is simply a spawn of a StaticMessActor via SpawnActor and then ssigment of a default cube primitive. I still dont understand how bIsSpatiallyLoaded is relevant outside of the editor, the property is literally not compiled in Shipping. Besides, any dynamically spawned actor is rooted to/owned by root PersistentLevel, it is not part of any sublevel.

Setting bAlwaysRelevant on spawned actor solves the issue but this is not a proper solution because it pollutes replication bandwidth.

To test increase the cell size and loading range in world partition.

Is this doable at runtime?

What Class is calling the spawning of the SMA?


When you drag a mesh in to the level the editor by default creates a static mesh actor and sets your dragged in mesh as a component.

No different that this

Static Mesh Actors are set to be Spatially Loaded.

When you package your UE5 project, all the data associated with your World Partition, including the spatially loaded actors and their associated cells, is included in the build. The engine then handles the loading and unloading of these elements at runtime based on the player’s location.

Okay, let’s see.
I made a dummy Actor. I left isSpatiallyLoaded in default state. I cooked it.
Then I disabled spatially loaded and cooked it again. Compared 2 files- they are binary equivalent.

We learned it is not serialized into an asset. Good. But you claim it is, so where could it be? May be WP level data? Sure, except this specific actor isnt part of ANY level or sublevel at all, and the spatial flag is per instance, not per type.

Moreover, I could not find ANY place in the code where spatial flag would be serialized at all for shipping, all of the code related to it is not even compiled outside of EDITOR. You can’t even compile in Shipping your code that tries to access that property.
If you have one - please show me, may be it is indirect, somewhere .

World Partition is an automatic data management and distance-based level streaming system. Is Spatially Loaded is the primary flag telling WP whether or not to load or unload an actor.

  • If enabled, this Actor is loaded when in range of any streaming source when not assigned to a disabled Data Layer.
  • If disabled, this Actor is loaded when not assigned to a disabled Data Layer.

Your controller is the default Streaming Source.


Actors placed in the level have their data stored in the level data when the level is cooked.


OWNERSHIP

Ownership of spawned actors directly affects whether or not WP will or will not manage loading and unloading of the actor.

For discussion purposes let’s say the “Spawner Class” is placed in the level and is set to “Spatially Loaded”.

When spawning an actor from this class…

Owner (Self) = Forces to Owners preference.
Owner (Null) = Ignores World Partition… Forced Non-Spatially Loaded.

Replicated Spawned Actors

Owner (Self) = Forces to Owners preference, Netcull distance will be ignored. Meaning if the owner is unloaded, then the replicated actor will be as well.

Owner (Null) = Discards WP altogether and Replication SubSystem takes over. Spatially Loaded has no bearing.

Owner being Game Mode, Game State or any other class that is not managed by WP will be non-spatially loaded.


I can thoroughly demonstrate this if you want.