Initialize components in C++ the correct way

Is this following piece of code correct:

// some .h file

class AMyCustomActor : public AActor
{
    GENERATED_BODY()

public:
    AMyCustomActor::AMyCustomActor()
    {
        Root = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	    SetRootComponent(Root);
  
        SomeStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SomeStaticMesh"));
        SomeStaticMesh->SetupAttachment(Root);
        
        SomeActorComponent = CreateDefaultSubObject<UActorComponent>(TEXT("SomeActorComponent"));
        SomeActorComponent->RegisterComponent();
    };

protected:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<USceneComponent> Root;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UStaticMeshComponent> SomeStaticMesh;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UActorComponent> SomeActorComponent;

    // ...
  • Is SetupAttachment necessary and sufficient for any scene component?
  • Is RegisterComponent necessary and sufficient for any none-scene component?

Thanks

And for bonus points:

  • Is RegisterComponent always redundant after SetupAttachment (for scene components)?
  • What does AddInstanceComponent add to the mix?

I usually open up classes like Character.cpp and see how they created the character movement component etc.

No need to call RegisterComponent in constructor, this is only needed when creating the component dynamically at runtime, for example at begin play or after an even occurs in game.

Setup attachment is also preferred in constructor, however at runtime you’d use attach component to component.

Thanks for the hint. Indeed, I can check out existing code to get a more definite idea.

I have read that RegisterComponent is not needed in the constructor. But does that mean that actor components don’t need any further step? They will show up in the editor anyway?

Also, using SetupAttachment when creating components at runtime does work for me, too, in which case RegisterComponent seems to be redundant there, too. So I am still not sure, what I am doing really and just trying out to see what works.

Testing after taking in account @KaidoomDev 's advice results in this correct code for defining components in C++ at build time:

// some .h file

class AMyCustomActor : public AActor
{
    GENERATED_BODY()

public:
    AMyCustomActor::AMyCustomActor()
    {
        Root = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	    SetRootComponent(Root);
  
        SomeStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SomeStaticMesh"));
        SomeStaticMesh->SetupAttachment(Root);
        
        SomeActorComponent = CreateDefaultSubObject<UActorComponent>(TEXT("SomeActorComponent"));
    };

protected:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<USceneComponent> Root;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UStaticMeshComponent> SomeStaticMesh;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    TObjectPtr<UActorComponent> SomeActorComponent;

    // ...

And at runtime, the following works:

    // the `FName` needs to be unique, therefore I construct it using the loop index `i`
    const auto SplineMesh = NewObject<USplineMeshComponent>(this, *FString(TEXT("SplineMesh")).Append(FString::FromInt(i)));
    // if I don't register here, the spline mesh doesn't render
	SplineMesh->RegisterComponent();
	SplineMesh->AttachToComponent(Root, FAttachmentTransformRules::KeepWorldTransform);
	// if I don't add instance here, the spline meshes don't show in the component list in the editor
	AddInstanceComponent(SplineMesh);
		
    // specific set up of spline mesh component
	SplineMesh->SetMobility(EComponentMobility::Stationary);
	SplineMesh->CastShadow = false;
	SplineMesh->SetStaticMesh(SM_Trajectory);
	const auto VecStartPos = Spline->GetLocationAtSplinePoint(Indices[i], ESplineCoordinateSpace::World);
	const auto VecStartDirection = Spline->GetTangentAtSplinePoint(Indices[i], ESplineCoordinateSpace::World);
	const auto VecEndPos = Spline->GetLocationAtSplinePoint(Indices[i + 1], ESplineCoordinateSpace::World);
	const auto VecEndDirection = Spline->GetTangentAtSplinePoint(Indices[i + 1], ESplineCoordinateSpace::World);
	SplineMesh->SetStartAndEnd(VecStartPos, VecStartDirection, VecEndPos, VecEndDirection);
3 Likes