Nested scene component subobjects attach incorrectly in child blueprints

Hi everyone,

I have encountered a weird issue when trying to create custom scene components with nested scene components as their default subobjects. The following simplified example is based on the 5.2.1 release version of the engine:

First, we create a custom scene component with another scene component that we attach to it as a default subobject. I am using UBillboardComponent for the child component here for easy visual representation.

// MyComponent.h
private:
    UPROPERTY(EditAnywhere)
    TObjectPtr<UTexture2D> Sprite;
	
    UPROPERTY()
    TObjectPtr<UBillboardComponent> ChildComponent;
	
public:
    UMyComponent();

    virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;

// MyComponent.cpp
UMyComponent::UMyComponent()
{
    ChildComponent = CreateDefaultSubobject<UBillboardComponent>(TEXT("Child"));
    ChildComponent->SetupAttachment(this);
}

void UMyComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
    Super::PostEditChangeProperty(PropertyChangedEvent);

    // apply the selected sprite texture
    ChildComponent->Sprite = Sprite;
    ChildComponent->MarkRenderStateDirty();
}

Then we create a custom actor and add the created scene component to it as a default subobject.

// MyActor.h
private:
    UPROPERTY(EditAnywhere)
    TObjectPtr<UMyComponent> MyComponent;
	
public:
    AMyActor();

// MyActor.cpp
AMyActor::AMyActor()
{
    MyComponent = CreateDefaultSubobject<UMyComponent>(TEXT("MyComponent"));
    RootComponent = MyComponent;
}

Now when we spawn this actor in a level it behaves as expected, where the child component has the correct offset (zero offset in this case) to its parent component and the owning actor. The issue comes up when we create a child blueprint of the custom actor and place that in the level.

scene_components

As we can see on the image, the C++ actor (on the left) has the nested scene component attached correctly while its blueprint child (on the right) does not. Any nested components in fact end up being placed in the world origin.

When debugging this issue, I have found out that this is caused by an incorrect referencing of the parent component when the child component is trying to process its attachment. For some reason, when the component instance is being created, the attachment is not remapped to the owning parent component instance but instead it still points to the CDO. As a result the child component instance attempts to attach itself to the parent component in the CDO, which fails. This is confirmed by the resulting error message produced by the assert in SceneComponent.cpp at line 1998.

The only solution I have been able to find is to manually redirect the attachment to the desired parent component instance by calling SetupAttachment() again inside the OnRegister() function. With this the nested components in the blueprint actor behave the same way as in the original C++ actor.

// MyComponent.cpp
void UMyComponent::OnRegister()
{
    Super::OnRegister();

    ChildComponent->SetupAttachment(this);
}

I am not entirely sure if this is a bug or the “intended” behavior where I have missed some important steps in order to apply the attachment correctly and my solution is just a band-aid that luckily works. Although any disscussion I have found about this topic did not mention anything beyond calling SetupAttachment(). I have also found a couple of different topics that mentioned the same or simillar issues but none provided me with a different solution.

Worst case scenario, if somebody else runs into this issue, this approach hopefully helps them. I am curious if anybody can shed some more light or provide more input regarding this issue.

2 Likes