Replace Root Component on an Inherited Class

Hi everyone!

So my question is: is it possible to replace the root component in a subclass if I’ve already changed it in the super class?

To explain more, I have a c++ class inheriting from Actor called BaseInteractable. I made the root component of BaseInteractable a SphereComponent. Now in a subclass of BaseInteractable, BaseInteractable_Child, I am trying to change the root component to a BoxComponent. But I am having issues with it. Does anyone know if a) this is possible to do and b) how I might do this?

Any help or advice is greatly appreciated thanks!

Alright never mind haha. It’s always right after you think you can’t do something that you find the solution.

So apparently the solution was very simple:

SuperClass.h

// Declare a sphere component 
UPROPERTY(VisibleDefaultsOnly, Category = Interact)
class USphereComponent* InteractArea;

SuperClass.cpp

// Create subobject in the constructor
InteractArea = CreateDefaultSubobject<USphereComponent>(TEXT("InteactArea"));
RootComponent = InteractArea;

SubClass.h

// Declare a new component to use as the new root component
UPROPERTY(VisibleDefaultsOnly, Category = Interact)
class UStaticMeshComponent* SM;

SubClass.cpp

// Create the subobject make it the new root component
SM = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SM"));

// Note that this makes SM the new root but it then attaches the previous root to SM
RootComponent = SM;

/* 
 * The above code makes SM the new root as mentioned, but then it effectually does:
 *      InteractArea->SetupAttachment(GetRootComponent());
 * 
 * At least that's what I'm observing. It could be doing something else.
 */

This is the behavior I was going for, but hopefully others will find this helpful too.

Yes you have to assign a new RootComponent and then call:

InteractArea->DestroyComponent(); // Unregisters and destroys the old RootComponent

Otherwise the old component will still exist and will still interfere with the gameplay which may not always be intended.

This works but is there a way to change it from blueprint? I tried construction node, didn’t work. I also tried setting a C++ variable to choose which root but the ctor doesn’t read the BP variable.

Looked a little bit into it,
if you are in C++ there is an easier way to change a component the important part is to set the component defined in the parent of a common root type of the new component.

e.g. if you want your class to support box and sphere collider I would recommend to use the next common parent which is a primitive component.

Then you would assign the component with a fixed name and can override it in the constructor in the C++ class.

TestActor.h

#pragma once

#include "GameFramework/Actor.h"

#include "TestActor.generated.h"

UCLASS(Blueprintable)
class PROPERTYGRAPHII_API ATestActor : public AActor
{
	GENERATED_BODY()

public:
	static const FName RootPrimitiveComponentName;
	
	explicit ATestActor(const FObjectInitializer& ObjectInitializer);
	
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	TObjectPtr<UPrimitiveComponent> RootPrimitiveComponent { nullptr };
};

UCLASS(Blueprintable)
class PROPERTYGRAPHII_API AChildTestActor : public ATestActor
{
	GENERATED_BODY()

public:
	explicit AChildTestActor(const FObjectInitializer& ObjectInitializer);
};

TestActor.cpp


#include "PropertyGraphII/TestActor.h"

#include "Components/BoxComponent.h"
#include "Components/SphereComponent.h"

const FName ATestActor::RootPrimitiveComponentName = TEXT("Root");

ATestActor::ATestActor(const FObjectInitializer& ObjectInitializer)
{
	RootPrimitiveComponent = Cast<UPrimitiveComponent>(CreateDefaultSubobject(RootPrimitiveComponentName, UPrimitiveComponent::StaticClass(),
		USphereComponent::StaticClass(), true, false));
	SetRootComponent(RootPrimitiveComponent);
}

AChildTestActor::AChildTestActor(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer.SetDefaultSubobjectClass<UBoxComponent>(ATestActor::RootPrimitiveComponentName))
{
}

The important part is

Super(ObjectInitializer.SetDefaultSubobjectClass<UBoxComponent>(ATestActor::RootPrimitiveComponentName))

which defines the new type for the component identified with ATestActor::RootPrimitiveComponentName by name.


Now to blueprint.
I couldn’t find an easy way how to do this in blueprint only the best thing I could find was to create a base C++ class with a defined component then you are able to change the class of the component in blueprints.

ABlueprintTestActor.h

#pragma once

#include "GameFramework/Actor.h"

#include "BlueprintTestActor.generated.h"

UCLASS(Blueprintable)
class PROPERTYGRAPHII_API ABlueprintTestActor : public AActor
{
	GENERATED_BODY()

public:
	static const FName OverridableRootComponentName;
	
	explicit ABlueprintTestActor(const FObjectInitializer& ObjectInitializer);
	
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	TObjectPtr<USceneComponent> OverridableRootComponent { nullptr };
};

ABlueprintTestActor.cpp

#include "PropertyGraphII/BlueprintTestActor.h"

const FName ABlueprintTestActor::OverridableRootComponentName = TEXT("Root");

ABlueprintTestActor::ABlueprintTestActor(const FObjectInitializer& ObjectInitializer)
{
	OverridableRootComponent = CreateDefaultSubobject<USceneComponent>(OverridableRootComponentName);
	SetRootComponent(OverridableRootComponent);
}

This allows you to change the component in blueprints by selecting a different component class.

The only issue that I found with this is that child blueprints can only go into more specialization.
Meaning that if you change the component to a sphere component you can only change the component to something even more specific in the child.


sphere component can get more specialized to a DrawSphereComponent but not to a box component.

So guess the lesson here is keep the root on a common type until you specialize.

Thanks, I think this is almost perfect