Adding components to an Actor to encapsulate functionality

I want to avoid that my Actor becomes a god class, therefore I encapsulate certain functionality into components. These components are added during runtime in PostInitializeComponents() since they have dependencies to other components which have to be set beforehand. I’ve used constructors but they are also called even before the game starts, which is unnecessary for me.

The best way I’ve found so far is to use OnRegister.

In AActor::PostInitializeComponents()


        this->featureOne = NewObject<UMyFeatureOne>(this);
        this->featureOne->RegisterComponent();

and putting the initialization code of the component into


void OnRegister() override;

Advantage of that is that once BeginPlay() is invoked at the parent Actor, also BeginPlay() on the registered component is called. [HR][/HR]
Are there better ways to add components to Actors (e.g. as UObjects or using UActorComponent::InitializeComponent() or …)? The requirements are:

  • The component doesn’t need to have physics nor a visual representation in the game world.
  • The components lifetime should be dependent on the Actors lifetime.
  • The component is attached during runtime when certain conditions are met.
  • The component should support replication.
  • It doesn’t matter if it is visible in the editor or not.

Having components added during construction isn’t a bad idea. This allows the class’s CDO to already have all the components you need, which in most cases is what you want. This makes sense when setting up the default components your Actor needs.

If that is all you need, here is how to do it in the constructor:
If UActorComponent :


CreateDefaultSubobject<USomeActorComponent>(TEXT("ActorComponentName"));

If USceneComponent:



auto component = CreateDefaultSubobject<USomeSceneComponent>(TEXT("ActorComponentName"));
component->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);


If you want components that are added and removed at runtime, you are taking the right approach but I would set the


CreationMethod = EComponentCreationMethod::Native;

and if it is a USceneComponent, you want to attach it to something.

eg.



auto boxComponent = NewObject<UBoxComponent>(this,  TEXT("Some Box"));
boxComponent->CreationMethod = EComponentCreationMethod::Native;
boxComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
boxComponent->RegisterComponent();


If components depend on each other, you can simply write an Initialization function for your component and pass them whatever they need in either OnConstruct or PostInitializeComponents.

eg.

UCLASS()
class USomeActorComponent: public UActorComponent
{
GENERATED_BODY()
public:
void Initialize(USceneComponent* sceneComponent);
};



ASomeActor::ASomeActor()
{
CreateDefaultSubobject<USomeActorComponent>(TEXT("ActorComponentName"));
}

ASomeActor::PostInitializeComponents()
{
if(auto someActorComponent = FindComponentByClass<USomeActorComponent>())
someActorComponent->Initialize(RootComponent);
}


Hope this helps.
There are many ways of initializing components. It shouldn’t matter too much what you choose as long as it works.

4 Likes

Thanks for mentioning EComponentCreationMethod, didn’t know that before. However what is the benefit of setting this?

tldr; Don’t remember why other than to avoid a crash I was having.

I developed a something that used a spline of arbitrary length and then filled it with an arbitrary number of spline mesh components at runtime. The spline changed at runtime and I was pooling spline mesh components. I was running into crashes.

I think I ended up on this page:

The other nice thing about adding actor components in the constructor is that the Editor preview image will look like it does in game.