Cooking with IsEditorOnlyActor parent moves children to origin

Hello, I have included a repro which shows a problem where if you have an IsEditorOnlyActor parent and you then cook, then the children get offset from origin, instead of where the parent was during the cook. If you run it in PIE you see that the children stay at the place you expect them to be in the world, and it’s just the parent that gets removed and the children stay in place.

I would expect that the children should behave the same in a cook, so they should not get teleported to origin and rather stay in the location they were placed in.

For additional context:

In our project this issue is essentially random in the cook. So sometimes the children appear in the correct place after the cook, and other times they show up at origin. So for larger projects there seems to be some race condition involved, for whatever reason. However for this minimal repro it seems to reproduce on every single cook.

I have reproduced this issue in our custom 5.4 & 5.5.4 editor, as well as “vanilla” 5.5.4 and 5.6.1 and it behaves identically in all of them (children get moved to origin in a cook).

Our own investigations showed that we could work around this issue by making sure the children get detached during the cook and that they inherit the world transform from the parent. For example by modifying AActor::ClearCrossLevelReferences(), and ensuring it calls DetachFromComponent for the children with KeepWorldTransform. I doubt that is the correct fix however.

For now we are currently not setting the parent as IsEditorOnlyActor to ensure that the cooked location is deterministic, but that is just a temporary workaround and causes problems in other areas of the project, so we do need some way of fixing this before it is time to ship.

Thanks

Steps to Reproduce
With the uploaded repro project do the following:

  1. Start the level in PIE. Observe that the large parent cube is not visible (it is IsEditorOnlyActor) and that the remaining cube and the sphere are not next to each other
  2. Now cook and run the level. Now you will see that the large parent cube is still gone, however now the child cube is next to the sphere.

The sphere is just there to highlight where origin is.

Expected behavior:

The child cube should remain in it’s original location and not be moved to be relative to origin.

Hello!

We have been able to reproduce the problem thanks to the sample you shared. I will file a bug report so this can be investigated by the development team.

What is the use case for the editor only parent? Could grouping be suitable for your needs? You can group actors by selecting them and hitting CTRL+G or using the option in the right-click menu.

For more details: https://dev.epicgames.com/documentation/en\-us/unreal\-engine/grouping\-actors\-in\-unreal\-engine

Regards,

Martin

I already had tested in a WP level and reproduced the problem. I’m pretty sure the problem is the same for both cases. FYI, the bug report should soon be visible at: https://issues.unrealengine.com/issue/UE-347835

I discussed the workarounds you found with a colleague and we are not sure they are safe. We fear they could have some undesirable side-effects.

As you are using WP, is there a reason why you need this alternative “grouping” solution to LevelInstances and PackedLevelActors?

Thanks for the feedback. I transmitted it to the Word Building team.

Martin

Hello,

Nice to hear that you were able to reproduce it.

Regarding your question, about the use-case, I have this snippet written by someone who knows more about the workflows involved:

Our team has been looking into ways to improve the workflow of working with multiple actors at once in Unreal. Unreal’s built-in grouping tool gave us a starting point, but as we were using it, we noticed a few areas where it didn’t quite match our team’s needs:

  • Outliner organization*: When creating a group from a set of actors, it is placed alphabetically as an individual actor in the outliner, which oftentimes separates it completely from the actors it contains.*
  • Workflow efficiency*: All actions for working with groups could only be performed by right clicking an actor, except for grouping and ungrouping, which could be done using Ctrl + G. We wanted faster ways to add to / remove from groups, locking and unlocking groups, along with a transformable pivot that’s clearly represented in the viewport with its own icon. Overall, we wanted to facilitate being able to work without having to click into menus to perform actions users would wanna perform continuously.*

So what we have is essentially a customized version of Group Actor, which is set to isEditorOnlyActor and allowing it to be the parent of the actors that are grouped under it. So that the whole group can easily be transformed and pivoted from that actor etc.

I hope this answers your question.

I have some additional information about the bug, which you may find useful.

We essentially managed to identify where this origin offset issue seems to happen and made a temporary hotfix for making sure that they get offset correctly.

This was produced while debugging it in our game project (which uses World Partition), where we saw it was only reproducible inconsistently.

We traced it down to calls to `FLevelUtils::ApplyLevelTransform()` and the issue stems from the following conditions:

  1. TransformParams.Actor != nullptr
  2. TransformParams.bSetRelativeTransformDirectly = false
  3. RootComponent->GetAttachParent() != nullptr
  4. RootComponent->GetAttachParentActor()->IsEditorOnly()

The interesting points here are number 3 and 4. So in this case, for whatever reason some Actors end up in this flow, where they still have a parent set and that parent IsEditorOnly() == true. This causes ApplyLevelTransform() to do nothing at all, which then results in the parent being removed from the cooked build and thus they have the incorrect offsets.

So the hotfix involved also checking `RootComponent->GetAttachParentActor() && RootComponent->GetAttachParentActor()->IsEditorOnly()`, if that is true it still applies the transform instead of skipping it.

As for how it gets into this state in the first place, I am not sure. There seems to be some race condition aspect of it for our particular setup.

Let me know if you need a World Partition repro case as well and I can try to produce one of those.

I am not sure if the basic problem is the same for both scenarios, and that the fix you produce would also fix the scenario we are seeing with WP.

Thanks for linking the bug report, as well as the heads up on the workaround risks.

We have tested the workaround extensively and thus far we have not found any downsides to it and we will likely try to go with that until we have a proper fix, since it is causing headaches for us. We will be keeping an eye on it though, at least we can log when the fix triggers so we should be able to identify when it causes us problems.

Regarding the alternative grouping solutions here’s a list of reasons; hope it helps:

  1. Editing of content in LIs is cumbersome and can be restrictive
  2. We’ve been advised not to use Level Instances to just organise content as there’s a performance overhead
  3. There was a slowness in validating content when we had organised all of locations into LIs
  4. Adding/deleting/cutting content between LIs (or from the larger world into a LI ) is incredibly difficult to do
  5. Accessing information about Actors inside a Level Instance is difficult
  6. Nesting LIs wasn’t possible (not sure if this has been updated since)
  7. We have been told by Epic that LIs will not be developed further
  8. PLAs issues are that they are difficult to edit with stepping into both up and down the chain (especially if nesting)