Summary
Ctrl+D, Alt-drag and copy/paste of an actor whose PCG component generates at runtime (Generation Trigger: Generate At Runtime) copy the actor’s live generated components into the duplicate. On the source that output is Preview-mode and RF_Transient, so a save writes it as None. On the copy it arrives as ordinary, non-transient instance components, together with the managed-resource records that claim them, and it saves with the level. When the level is reopened, the copy regenerates into that saved component without clearing it, so the copy shows every mesh twice. Saving once more drops the stale data.
Cause, from reading the source: edactDuplicateSelected calls DuplicateActors, which round-trips the actors through text with UUnrealEdEngine::CopyActors and PasteActors (EditorActor.cpp:741, 751). UExporter::ExportObjectInner (UnrealExporter.cpp:606, 618) exports every inner object unless it has RF_TextExportTransient; it never checks RF_Transient. UPCGActorHelpers::GetOrCreateManagedISMC gives Preview output RF_Transient | RF_NonPIEDuplicateTransient (PCGActorHelpers.cpp:232; also 378 and 549), so the output is exported, and the import recreates it without either flag. RF_NonPIEDuplicateTransient applies only to StaticDuplicateObject, which this path never runs.
Possible fix (not tested): stamp RF_TextExportTransient on Preview-mode output as well, or have UPCGComponent::PostEditImport release the generated resources it arrives with.
What Type of Bug are you experiencing?
Editor
Steps to Reproduce
- Create a PCG graph: Create Points Grid (Grid Extents 300, 300, 0; Cell Size 200, 200, 100) into Static Mesh Spawner, with mesh /Engine/BasicShapes/Cube.
- In an empty level, place a PCG Volume, scale it to 4, 4, 1, assign the graph, and set Generation Trigger to Generate At Runtime. Wait for the 9 cubes to appear.
- Select the volume and press Ctrl+D (or Alt-drag it). Move the copy clear of the original. Save the level.
- Reopen the level.
Expected Result
Both volumes show 9 cubes, and the saved level holds no generated components for either.
Observed Result
- After step 3, the saved level holds the copy’s ISM_Cube_0 as an instance component, and the copy’s PCG component claims it through a saved PCGManagedISMComponent. The original’s output is saved as None, as expected.
- After step 4, the copy’s ISM_Cube_0 holds 18 instances (two cubes at each point), while the original’s holds 9.
- Saving once more drops the stale component, and the next open shows 9.
Measured with editor Python (instance counts read from the components) and a T3D export of the saved package. Tested on UE 5.8.3 (CL 58210709, launcher build), macOS 27.0.1, Apple Silicon.
Affects Versions
5.8
Platform(s)
Mac
Additional Notes
How long the copy survives depends on what claims it. In the steps above, the copy’s own PCG component claims it through the saved record. On load, UPCGComponent::PostLoad forces a Generate At Runtime component into Preview mode and stamps RF_Transient on the output that record claims (ChangeTransientState), so the next save drops it.
Our level-design plugin also creates a transient PCG component at runtime, for an editor preview. That component is copied too, but nothing serialized references it, so it is never saved, and the output copies it claimed load unclaimed and permanent: a duplicated room showed its dressing twice on every open until the copies were removed. We now remove non-transient generated output from pasted actors ourselves.
Copy/paste (Ctrl+C, Ctrl+V) goes through the same UUnrealEdEngine::CopyActors. We did not run it, because it overwrites the system clipboard.