Creating UChildActorComponent with class from BP is crashing

Im trying to migrate my dirty BP Construnction Script with creation of actor with child actors into C++ code.

I added UActorComponent to handle this process.

in .h:

private:
	USceneComponent *AimHelperPlaceholder = nullptr;
	USceneComponent *FrontGunPlaceholder = nullptr;
public:
	UPROPERTY(BlueprintReadWrite, Category = "Components")
	AShipGunActor *FrontGun = nullptr;

	UPROPERTY(BlueprintReadWrite, Category = "Components")
	AAimDeviceActor *AimHelper = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Setup, meta = (ExposeOnSpawn = true))
	TSubclassOf<AShipGunActor> FrontGunBlueprint;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Setup, meta = (ExposeOnSpawn = true))
	TSubclassOf<AAimDeviceActor> AimHelperBlueprint;

My C++ code:

void UAimingComponent::Setup(USceneComponent *AimHelperPlaceholderToSet, USceneComponent *FrontGunPlaceholderToSet)
{
	this->AimHelperPlaceholder = AimHelperPlaceholderToSet;
	this->FrontGunPlaceholder = FrontGunPlaceholderToSet;
	if (AimHelperPlaceholder && FrontGunPlaceholder)
	{
		if (!AimHelper)
		{
			if (AimHelperBlueprint)
			{
				UChildActorComponent *ChildAimActor = CreateDefaultSubobject<UChildActorComponent>(TEXT("AimComponent"));
				ChildAimActor->SetChildActorClass(AimHelperBlueprint);
				ChildAimActor->CreateChildActor();
				ChildAimActor->AttachToComponent(AimHelperPlaceholder, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, true));
			}
		}
	}
}

Setup() called on BP Construnction script instead BP nodes from screenshot. It crasshes on CreateDefaultSubObject.
What am I doing wrong?

CreateDefaultSubObject shouldn’t be used outside of the C++ constructor.

Instead, try:

UChildActorComponent *ChildAimActor = NewObject<UChildActorComponent>(this, TEXT("AimComponent"));

Also, if you’re using the AimHelperPlaceholder or FrontGunPlaceholder pointers elsewhere, they won’t be protected from garbage collection without including UPROPERTY:

UPROPERTY()
USceneComponent *AimHelperPlaceholder = nullptr;

UPROPERTY()
USceneComponent *FrontGunPlaceholder = nullptr;

If you’re not using them elsewhere, get rid of them. Why are the being assigned in this function? As long as you have access to those pointers outside the function you can just used the passed-in pointers to attach the component.

If this doesn’t fix your crash, post the contents of your log so we can see what’s going on and where the crash occurred.

I modified code and child actor component doesn’t appear.

.h file:

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components")
UChildActorComponent *ThrottleControl = nullptr;

cpp file:

void UShipMovementComponent::Setup(USceneComponent *ThrottleControlComponent, TSubclassOf<AThrottleActor> ThrottleControlBlueprint, USceneComponent *SteeringWheelComponent, TSubclassOf<ASteeringWheelActor> SteeringWheelBlueprint)
{
	UE_LOG(LogTemp, Warning, TEXT("ShipMovementComponent::Setup"))
	if (!ThrottleControl && ThrottleControlBlueprint)
	{
		ThrottleControl = NewObject<UChildActorComponent>(this, TEXT("ThrottleControlComponentX"));
		ThrottleControl->SetChildActorClass(ThrottleControlBlueprint);
		//ThrottleControl->InitializeComponent(); THIS LINE CAUSE CRASH!
		ThrottleControl->AttachToComponent(ThrottleControlComponent, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, true));
	}
}

Funcion called inside Construction Script part of Blueprint, works without crash but actor doesn’t spawn. Adding InitializeComponent() cause crash: [2017.04.22-16.59.09:165][ 3]LogTemp:Warning: ShipMovementComponent::Setup - Pastebin.com

The BP construction script isn’t the same as the c++ constructor; it’s a bit confusing. The BP construction is actually an event called after the C++ object is created.

In order to use dynamically created components (those spawned outside the c++ constructor) you need to register the component the world, and set the actor that owns the component. You’ll need a pointer to the actor you want to add the component to (here I’ve used “Owner”). I also find it’s best practice to check for the existence of the new object before using it to detect spawning problems and avoid nullptr exceptions:

ThrottleControl = NewObject<UChildActorComponent>(this, TEXT("ThrottleControlComponentX"));
if (ThrottleControl) {
    ThrottleControl->RegisterComponentWithWorld(Owner->GetWorld());
    Owner->AddOwnedComponent(ThrottleControl);
    ...continue component initialization...
}

If you don’t have direct access to the owning actor, you can get it via GetOwner from another owned component.

AActor* Owner = ThrottleControlComponent->GetOwner();

You can register the component without sending the world explicitly (via RegisterComponent), but it will try to climb the chain to the world via its outer (owning) actor, so you need to have added the component to the actor before registering it in that case.

Thank you very much - I didn’t find in manuals info about RegisterComponentWithWorld(). Now it works :slight_smile:

Before as workaround I used in Begin section call to this code:

if (!ThrottleControl && ThrottleControlBlueprint)
	{
		ThrottleControl = GetWorld()->SpawnActor<AThrottleActor>(ThrottleControlBlueprint, FTransform());
		ThrottleControl->Initialize(); 
		ThrottleControl->AttachToComponent(ThrottleControlComponent, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, true));
		CreatedActors.Add(ThrottleControl);
	}