CreateDefaultSubobject becomes nullptr before reaching BeginPlay()

Here is some code:

PlayerCharacter.h

public:
	//overloading OnOverlap for the sphere collision
	UFUNCTION()
		void OnOverlapBegin(class UPrimitiveComponent* OverlappedComponent, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
		void OnOverlapEnd(class UPrimitiveComponent* OverlappedComponent, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

private:
UPROPERTY(EditDefaultsOnly, Category = "Switch Components")
	class USphereComponent* EnemyTriggerVolume = nullptr;

PlayerCharacter.cpp

APlayerCharacter::APlayerCharacter()
{

	PrimaryActorTick.bCanEverTick = true;

	EnemyTriggerVolume = CreateDefaultSubobject<USphereComponent>(TEXT("EnemyTriggerVolume"));
}

void APlayerCharacter::BeginPlay()
{
	Super::BeginPlay();
	//EnemyTriggerVolume = NewObject<USphereComponent>(TEXT("EnemyTriggerVolume"));
	if (EnemyTriggerVolume != nullptr)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("RUNNING"));
		EnemyTriggerVolume->SetSphereRadius(EnemyTriggerRadius);
		EnemyTriggerVolume->OnComponentBeginOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapBegin);
		EnemyTriggerVolume->OnComponentEndOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapEnd);
	}
	else 
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("NOT RUNNING"));
	}
	
}

ā€œNOT RUNNINGā€ plays because EnemyTriggerVolume is nullptr.

Am I approaching CreateDefaultSubobject incorrectly?

Hi,

From my experience, you should write these lines in the constructor as well:

 EnemyTriggerVolume->SetSphereRadius(EnemyTriggerRadius);
 EnemyTriggerVolume->OnComponentBeginOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapBegin);
 EnemyTriggerVolume->OnComponentEndOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapEnd);

Here is some example code from my side project where I use a sphere component and collision:

.h

protected:

	UPROPERTY(VisibleAnywhere)
	class USphereComponent* ItemCollision;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	class USphereComponent* ItemDetectionSphere;

protected:

	UFUNCTION()
		void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
			UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
			const FHitResult& SweepResult);

	UFUNCTION()
		void OnDetectionBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
			UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
			const FHitResult& SweepResult);

.cpp

#include "Hero/Item/Collectible/HeroItemCollectible.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "GameFramework/RotatingMovementComponent.h"
#include "Hero/PlayerCharacter/HeroPlayerCharacter.h"
#include "Kismet/KismetMathLibrary.h"
#include "Components/TimelineComponent.h"
#include "Runtime/Engine/Classes/Components/SphereComponent.h"

AHeroItemCollectible::AHeroItemCollectible()
{
	SetActorTickEnabled(true);
	PrimaryActorTick.bCanEverTick = true;

	ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotatingMovement"));

	ItemCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ItemCollision"));
	ItemDetectionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("ItemDetectionSphere"));

	ItemCollision->SetupAttachment(ItemMesh);
	ItemDetectionSphere->SetupAttachment(ItemMesh);

	//No Collision by default for Item Detection
	ItemDetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	ItemCollision->OnComponentBeginOverlap.AddDynamic(this, &AHeroItemCollectible::OnOverlapBegin);
	ItemDetectionSphere->OnComponentBeginOverlap.AddDynamic(this, &AHeroItemCollectible::OnDetectionBegin);
}

void AHeroItemCollectible::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AHeroPlayerCharacter* Player = Cast<class AHeroPlayerCharacter>(OtherActor);
	if (Player)
	{
		Destroy();
	}
}

void AHeroItemCollectible::OnDetectionBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AHeroPlayerCharacter* Player = Cast<class AHeroPlayerCharacter>(OtherActor);
	if (Player)
	{
		PlayerCharacter = Player;
		if (fMoveToPlayerCurve)
		{
			ItemDetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			ProjectileMovement->Deactivate();
			RotatingMovement->Deactivate();

			FOnTimelineFloat TimelineProgress;
			TimelineProgress.BindUFunction(this, FName("TimelineFloatReturn"));
			MovementTimeline.AddInterpFloat(fMoveToPlayerCurve, TimelineProgress);
			MovementTimeline.PlayFromStart();
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("NO CURVE ASSIGNED"));
		}
	}
}

Ignore the actual logic that I am doing and observe where I am creating the default subobject and where I am setting up the bindings for overlap; all this takes place in the constructor. Additionally, make sure you are including:

#include "Runtime/Engine/Classes/Components/SphereComponent.h"

You donā€™t show your includes, so I canā€™t be certain if you have it; I assume you do if the code compiles. I hope this example helps you in some way, and good luck.

Thank you for the help Devin.

I noticed something weird. When I comment out the CreateDefaultSubobject lines and then compile (so it no longer shows up in the blueprint), then re-add the lines, compile again and run the game it would workā€¦but just for that one time. Next time I hit play, it wonā€™t work.

This got me thinking that perhaps Iā€™m dealing with a weird bug.

I ended up re-parenting my BP_PlayerCharacter to AActor then reparenting back to PlayerCharacter and now it works flawlessly.

Very curious and Iā€™m not sure exactly what was causing this bug.

Iā€™m still getting the hang of the blueprint and c++ workflow.

1 Like

I find this to be the case sometimes as well; when making certain changes in the C++ source/header file; especially when it comes to creating components, you may need to reparent your original BP actor again so that the class sort of ā€˜refreshesā€™. I am glad you were able to figure things out :slight_smile: