I’d like to provide a potential alternative, if you like dangerous things.
You can still create an inherited component tree, and change the root component post-construction. It is not exposed to blueprints, but in C++ it’s not that uncommon to use SetRootComponent to change the root component.
For example, this method is used in Unreal Tournament when turning player character into a ragdoll. By default the root component is the capsule, but for ragdolling Epic sets the skeletal mesh as root component and enables physics on it (and disables capsule).
In a similar fashion, you could declare a default Box root component, and change the root component post-construction to a different shape component (eg. sphere), then reattach the former root to your new root.
Considering you’ll probably want to bind events to the shape component which is gonna be set by blueprints, you cannot bind events in constructor. But you can do that in BeginPlay just fine.
Here is a rough attempt, that seems to work (probably… maybe)
UCLASS()
class ATestActor : public AActor
{
GENERATED_BODY()
ATestActor();
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UShapeComponent* ShapeComp;
UFUNCTION(BlueprintCallable)
void ReplaceRoot(USceneComponent* Comp);
virtual void BeginPlay() override;
UFUNCTION()
virtual void NativeOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};
ATestActor::ATestActor()
{
ShapeComp = CreateDefaultSubobject<UBoxComponent>("Box");
RootComponent = ShapeComp;
}
void ATestActor::ReplaceRoot(USceneComponent* Comp)
{
if (!Comp || Comp == RootComponent)
return;
auto OldRoot = GetRootComponent();
OldRoot->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
Comp->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
SetRootComponent(Comp);
OldRoot->AttachToComponent(Comp, FAttachmentTransformRules::KeepWorldTransform);
}
void ATestActor::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Display, TEXT("[BeginPlay] ShapeComp = %s"), *ShapeComp->GetName());
ShapeComp->OnComponentBeginOverlap.AddDynamic(this, &ATestActor::NativeOverlap);
}
void ATestActor::NativeOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
UE_LOG(LogTemp, Display, TEXT("[NativeOverlap] Comp = %s , Other = %s"), *OverlappedComponent->GetName(), *OtherActor->GetName());
}
Then this is my BP extending native class.
See the inherited root component. I added a static mesh for good measure, it could be inherited or not, doesn’t change anything.
I added a Sphere component below the root component :
Then in construction script I’m calling my replace root function, and assigning the sphere to the ShapeComp variable :
The blueprint viewport doesn’t show hierarchy changes, but when placing actor in the world the sphere effectively takes over the root, and box is attached back to the sphere :
Upon playtesting the logs show RootComponent is the sphere, ShapeComp is the sphere, and the events bound in BeginPlay are triggered from the sphere.